summaryrefslogtreecommitdiff
path: root/scene/gui
diff options
context:
space:
mode:
Diffstat (limited to 'scene/gui')
-rw-r--r--scene/gui/base_button.cpp15
-rw-r--r--scene/gui/base_button.h8
-rw-r--r--scene/gui/box_container.cpp13
-rw-r--r--scene/gui/box_container.h2
-rw-r--r--scene/gui/button.h2
-rw-r--r--scene/gui/center_container.h2
-rw-r--r--scene/gui/check_box.h2
-rw-r--r--scene/gui/check_button.h2
-rw-r--r--scene/gui/code_edit.cpp444
-rw-r--r--scene/gui/code_edit.h135
-rw-r--r--scene/gui/color_picker.cpp9
-rw-r--r--scene/gui/color_picker.h5
-rw-r--r--scene/gui/container.cpp4
-rw-r--r--scene/gui/container.h8
-rw-r--r--scene/gui/control.cpp17
-rw-r--r--scene/gui/control.h44
-rw-r--r--scene/gui/dialogs.cpp14
-rw-r--r--scene/gui/dialogs.h6
-rw-r--r--scene/gui/file_dialog.cpp30
-rw-r--r--scene/gui/file_dialog.h10
-rw-r--r--scene/gui/gradient_edit.h2
-rw-r--r--scene/gui/graph_edit.cpp155
-rw-r--r--scene/gui/graph_edit.h21
-rw-r--r--scene/gui/graph_node.cpp4
-rw-r--r--scene/gui/graph_node.h4
-rw-r--r--scene/gui/grid_container.h2
-rw-r--r--scene/gui/item_list.h4
-rw-r--r--scene/gui/label.cpp30
-rw-r--r--scene/gui/label.h2
-rw-r--r--scene/gui/line_edit.cpp113
-rw-r--r--scene/gui/line_edit.h21
-rw-r--r--scene/gui/link_button.h2
-rw-r--r--scene/gui/margin_container.h2
-rw-r--r--scene/gui/menu_button.h4
-rw-r--r--scene/gui/nine_patch_rect.h2
-rw-r--r--scene/gui/option_button.cpp3
-rw-r--r--scene/gui/option_button.h6
-rw-r--r--scene/gui/panel_container.h2
-rw-r--r--scene/gui/popup.cpp90
-rw-r--r--scene/gui/popup.h13
-rw-r--r--scene/gui/popup_menu.cpp403
-rw-r--r--scene/gui/popup_menu.h49
-rw-r--r--scene/gui/progress_bar.h2
-rw-r--r--scene/gui/range.h2
-rw-r--r--scene/gui/rich_text_effect.h4
-rw-r--r--scene/gui/rich_text_label.cpp379
-rw-r--r--scene/gui/rich_text_label.h17
-rw-r--r--scene/gui/scroll_bar.cpp23
-rw-r--r--scene/gui/scroll_bar.h24
-rw-r--r--scene/gui/scroll_container.h6
-rw-r--r--scene/gui/separator.h2
-rw-r--r--scene/gui/shortcut.cpp24
-rw-r--r--scene/gui/shortcut.h6
-rw-r--r--scene/gui/slider.h2
-rw-r--r--scene/gui/spin_box.cpp2
-rw-r--r--scene/gui/spin_box.h4
-rw-r--r--scene/gui/split_container.h4
-rw-r--r--scene/gui/subviewport_container.h2
-rw-r--r--scene/gui/tab_container.cpp72
-rw-r--r--scene/gui/tab_container.h17
-rw-r--r--scene/gui/tabs.cpp9
-rw-r--r--scene/gui/tabs.h10
-rw-r--r--scene/gui/text_edit.cpp1474
-rw-r--r--scene/gui/text_edit.h392
-rw-r--r--scene/gui/texture_button.cpp42
-rw-r--r--scene/gui/texture_button.h13
-rw-r--r--scene/gui/texture_progress.h2
-rw-r--r--scene/gui/texture_rect.h2
-rw-r--r--scene/gui/tool_button.cpp35
-rw-r--r--scene/gui/tool_button.h43
-rw-r--r--scene/gui/tree.cpp92
-rw-r--r--scene/gui/tree.h9
-rw-r--r--scene/gui/video_player.cpp4
-rw-r--r--scene/gui/video_player.h4
74 files changed, 2349 insertions, 2080 deletions
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp
index d8229b5f43..2e77d20d4e 100644
--- a/scene/gui/base_button.cpp
+++ b/scene/gui/base_button.cpp
@@ -60,7 +60,7 @@ void BaseButton::_gui_input(Ref<InputEvent> p_event) {
Ref<InputEventMouseButton> mouse_button = p_event;
bool ui_accept = p_event->is_action("ui_accept") && !p_event->is_echo();
- bool button_masked = mouse_button.is_valid() && ((1 << (mouse_button->get_button_index() - 1)) & button_mask) > 0;
+ bool button_masked = mouse_button.is_valid() && ((1 << (mouse_button->get_button_index() - 1)) & button_mask) != 0;
if (button_masked || ui_accept) {
on_action_event(p_event);
return;
@@ -271,6 +271,11 @@ BaseButton::DrawMode BaseButton::get_draw_mode() const {
}
void BaseButton::set_toggle_mode(bool p_on) {
+ // Make sure to set 'pressed' to false if we are not in toggle mode
+ if (!p_on) {
+ set_pressed(false);
+ }
+
toggle_mode = p_on;
}
@@ -321,12 +326,12 @@ bool BaseButton::is_keep_pressed_outside() const {
return keep_pressed_outside;
}
-void BaseButton::set_shortcut(const Ref<ShortCut> &p_shortcut) {
+void BaseButton::set_shortcut(const Ref<Shortcut> &p_shortcut) {
shortcut = p_shortcut;
set_process_unhandled_input(shortcut.is_valid());
}
-Ref<ShortCut> BaseButton::get_shortcut() const {
+Ref<Shortcut> BaseButton::get_shortcut() const {
return shortcut;
}
@@ -340,7 +345,7 @@ String BaseButton::get_tooltip(const Point2 &p_pos) const {
String tooltip = Control::get_tooltip(p_pos);
if (shortcut_in_tooltip && shortcut.is_valid() && shortcut->is_valid()) {
String text = shortcut->get_name() + " (" + shortcut->get_as_text() + ")";
- if (shortcut->get_name().nocasecmp_to(tooltip) != 0) {
+ if (tooltip != String() && shortcut->get_name().nocasecmp_to(tooltip) != 0) {
text += "\n" + tooltip;
}
tooltip = text;
@@ -409,7 +414,7 @@ void BaseButton::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "button_mask", PROPERTY_HINT_FLAGS, "Mouse Left, Mouse Right, Mouse Middle"), "set_button_mask", "get_button_mask");
ADD_PROPERTY(PropertyInfo(Variant::INT, "enabled_focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_enabled_focus_mode", "get_enabled_focus_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_pressed_outside"), "set_keep_pressed_outside", "is_keep_pressed_outside");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shortcut", PROPERTY_HINT_RESOURCE_TYPE, "ShortCut"), "set_shortcut", "get_shortcut");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shortcut", PROPERTY_HINT_RESOURCE_TYPE, "Shortcut"), "set_shortcut", "get_shortcut");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "group", PROPERTY_HINT_RESOURCE_TYPE, "ButtonGroup"), "set_button_group", "get_button_group");
BIND_ENUM_CONSTANT(DRAW_NORMAL);
diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h
index 05a975e266..8e71931f8b 100644
--- a/scene/gui/base_button.h
+++ b/scene/gui/base_button.h
@@ -50,7 +50,7 @@ private:
bool shortcut_in_tooltip;
bool keep_pressed_outside;
FocusMode enabled_focus_mode;
- Ref<ShortCut> shortcut;
+ Ref<Shortcut> shortcut;
ActionMode action_mode;
struct Status {
@@ -118,10 +118,10 @@ public:
void set_enabled_focus_mode(FocusMode p_mode);
FocusMode get_enabled_focus_mode() const;
- void set_shortcut(const Ref<ShortCut> &p_shortcut);
- Ref<ShortCut> get_shortcut() const;
+ void set_shortcut(const Ref<Shortcut> &p_shortcut);
+ Ref<Shortcut> get_shortcut() const;
- virtual String get_tooltip(const Point2 &p_pos) const;
+ virtual String get_tooltip(const Point2 &p_pos) const override;
void set_button_group(const Ref<ButtonGroup> &p_group);
Ref<ButtonGroup> get_button_group() const;
diff --git a/scene/gui/box_container.cpp b/scene/gui/box_container.cpp
index 75d04dba61..f130726837 100644
--- a/scene/gui/box_container.cpp
+++ b/scene/gui/box_container.cpp
@@ -96,7 +96,7 @@ void BoxContainer::_resort() {
}
stretch_avail += stretch_diff; //available stretch space.
- /** Second, pass sucessively to discard elements that can't be stretched, this will run while stretchable
+ /** Second, pass successively to discard elements that can't be stretched, this will run while stretchable
elements exist */
bool has_stretched = false;
@@ -104,6 +104,7 @@ void BoxContainer::_resort() {
has_stretched = true;
bool refit_successful = true; //assume refit-test will go well
+ float error = 0; // Keep track of accumulated error in pixels
for (int i = 0; i < get_child_count(); i++) {
Control *c = Object::cast_to<Control>(get_child(i));
@@ -119,8 +120,9 @@ void BoxContainer::_resort() {
if (msc.will_stretch) { //wants to stretch
//let's see if it can really stretch
-
- int final_pixel_size = stretch_avail * c->get_stretch_ratio() / stretch_ratio_total;
+ float final_pixel_size = stretch_avail * c->get_stretch_ratio() / stretch_ratio_total;
+ // Add leftover fractional pixels to error accumulator
+ error += final_pixel_size - (int)final_pixel_size;
if (final_pixel_size < msc.min_size) {
//if available stretching area is too small for widget,
//then remove it from stretching area
@@ -132,6 +134,11 @@ void BoxContainer::_resort() {
break;
} else {
msc.final_size = final_pixel_size;
+ // Dump accumulated error if one pixel or more
+ if (error >= 1) {
+ msc.final_size += 1;
+ error -= 1;
+ }
}
}
}
diff --git a/scene/gui/box_container.h b/scene/gui/box_container.h
index cc6f6349df..c4d75c3cf1 100644
--- a/scene/gui/box_container.h
+++ b/scene/gui/box_container.h
@@ -60,7 +60,7 @@ public:
void set_alignment(AlignMode p_align);
AlignMode get_alignment() const;
- virtual Size2 get_minimum_size() const;
+ virtual Size2 get_minimum_size() const override;
BoxContainer(bool p_vertical = false);
};
diff --git a/scene/gui/button.h b/scene/gui/button.h
index e757badd3e..5b44b1322e 100644
--- a/scene/gui/button.h
+++ b/scene/gui/button.h
@@ -59,7 +59,7 @@ protected:
static void _bind_methods();
public:
- virtual Size2 get_minimum_size() const;
+ virtual Size2 get_minimum_size() const override;
void set_text(const String &p_text);
String get_text() const;
diff --git a/scene/gui/center_container.h b/scene/gui/center_container.h
index ae9f87db16..638843c389 100644
--- a/scene/gui/center_container.h
+++ b/scene/gui/center_container.h
@@ -46,7 +46,7 @@ public:
void set_use_top_left(bool p_enable);
bool is_using_top_left() const;
- virtual Size2 get_minimum_size() const;
+ virtual Size2 get_minimum_size() const override;
CenterContainer();
};
diff --git a/scene/gui/check_box.h b/scene/gui/check_box.h
index 58f7cce55e..cc00524698 100644
--- a/scene/gui/check_box.h
+++ b/scene/gui/check_box.h
@@ -40,7 +40,7 @@ class CheckBox : public Button {
protected:
Size2 get_icon_size() const;
- Size2 get_minimum_size() const;
+ Size2 get_minimum_size() const override;
void _notification(int p_what);
bool is_radio();
diff --git a/scene/gui/check_button.h b/scene/gui/check_button.h
index 8bbad0b4b3..99a12a3270 100644
--- a/scene/gui/check_button.h
+++ b/scene/gui/check_button.h
@@ -40,7 +40,7 @@ class CheckButton : public Button {
protected:
Size2 get_icon_size() const;
- virtual Size2 get_minimum_size() const;
+ virtual Size2 get_minimum_size() const override;
void _notification(int p_what);
public:
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
new file mode 100644
index 0000000000..56cf432b38
--- /dev/null
+++ b/scene/gui/code_edit.cpp
@@ -0,0 +1,444 @@
+/*************************************************************************/
+/* code_edit.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 "code_edit.h"
+
+void CodeEdit::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_THEME_CHANGED:
+ case NOTIFICATION_ENTER_TREE: {
+ set_gutter_width(main_gutter, cache.row_height);
+ set_gutter_width(line_number_gutter, (line_number_digits + 1) * cache.font->get_char_size('0').width);
+ set_gutter_width(fold_gutter, cache.row_height / 1.2);
+
+ breakpoint_color = get_theme_color("breakpoint_color");
+ breakpoint_icon = get_theme_icon("breakpoint");
+
+ bookmark_color = get_theme_color("bookmark_color");
+ bookmark_icon = get_theme_icon("bookmark");
+
+ executing_line_color = get_theme_color("executing_line_color");
+ executing_line_icon = get_theme_icon("executing_line");
+
+ line_number_color = get_theme_color("line_number_color");
+
+ folding_color = get_theme_color("code_folding_color");
+ can_fold_icon = get_theme_icon("can_fold");
+ folded_icon = get_theme_icon("folded");
+ } break;
+ case NOTIFICATION_DRAW: {
+ } break;
+ }
+}
+
+/* Main Gutter */
+void CodeEdit::_update_draw_main_gutter() {
+ set_gutter_draw(main_gutter, draw_breakpoints || draw_bookmarks || draw_executing_lines);
+}
+
+void CodeEdit::set_draw_breakpoints_gutter(bool p_draw) {
+ draw_breakpoints = p_draw;
+ set_gutter_clickable(main_gutter, p_draw);
+ _update_draw_main_gutter();
+}
+
+bool CodeEdit::is_drawing_breakpoints_gutter() const {
+ return draw_breakpoints;
+}
+
+void CodeEdit::set_draw_bookmarks_gutter(bool p_draw) {
+ draw_bookmarks = p_draw;
+ _update_draw_main_gutter();
+}
+
+bool CodeEdit::is_drawing_bookmarks_gutter() const {
+ return draw_bookmarks;
+}
+
+void CodeEdit::set_draw_executing_lines_gutter(bool p_draw) {
+ draw_executing_lines = p_draw;
+ _update_draw_main_gutter();
+}
+
+bool CodeEdit::is_drawing_executing_lines_gutter() const {
+ return draw_executing_lines;
+}
+
+void CodeEdit::_main_gutter_draw_callback(int p_line, int p_gutter, const Rect2 &p_region) {
+ if (draw_breakpoints && is_line_breakpointed(p_line)) {
+ int padding = p_region.size.x / 6;
+
+ Rect2 breakpoint_region = p_region;
+ breakpoint_region.position += Point2(padding, padding);
+ breakpoint_region.size -= Point2(padding, padding) * 2;
+ breakpoint_icon->draw_rect(get_canvas_item(), breakpoint_region, false, breakpoint_color);
+ }
+
+ if (draw_bookmarks && is_line_bookmarked(p_line)) {
+ int horizontal_padding = p_region.size.x / 2;
+ int vertical_padding = p_region.size.y / 4;
+
+ Rect2 bookmark_region = p_region;
+ bookmark_region.position += Point2(horizontal_padding, 0);
+ bookmark_region.size -= Point2(horizontal_padding * 1.1, vertical_padding);
+ bookmark_icon->draw_rect(get_canvas_item(), bookmark_region, false, bookmark_color);
+ }
+
+ if (draw_executing_lines && is_line_executing(p_line)) {
+ int horizontal_padding = p_region.size.x / 10;
+ int vertical_padding = p_region.size.y / 4;
+
+ Rect2 executing_line_region = p_region;
+ executing_line_region.position += Point2(horizontal_padding, vertical_padding);
+ executing_line_region.size -= Point2(horizontal_padding, vertical_padding) * 2;
+ executing_line_icon->draw_rect(get_canvas_item(), executing_line_region, false, executing_line_color);
+ }
+}
+
+// Breakpoints
+void CodeEdit::set_line_as_breakpoint(int p_line, bool p_breakpointed) {
+ int mask = get_line_gutter_metadata(p_line, main_gutter);
+ set_line_gutter_metadata(p_line, main_gutter, p_breakpointed ? mask | MAIN_GUTTER_BREAKPOINT : mask & ~MAIN_GUTTER_BREAKPOINT);
+ if (p_breakpointed) {
+ breakpointed_lines[p_line] = true;
+ } else if (breakpointed_lines.has(p_line)) {
+ breakpointed_lines.erase(p_line);
+ }
+ emit_signal("breakpoint_toggled", p_line);
+}
+
+bool CodeEdit::is_line_breakpointed(int p_line) const {
+ return (int)get_line_gutter_metadata(p_line, main_gutter) & MAIN_GUTTER_BREAKPOINT;
+}
+
+void CodeEdit::clear_breakpointed_lines() {
+ for (int i = 0; i < get_line_count(); i++) {
+ if (is_line_breakpointed(i)) {
+ set_line_as_breakpoint(i, false);
+ }
+ }
+}
+
+Array CodeEdit::get_breakpointed_lines() const {
+ Array ret;
+ for (int i = 0; i < get_line_count(); i++) {
+ if (is_line_breakpointed(i)) {
+ ret.append(i);
+ }
+ }
+ return ret;
+}
+
+// Bookmarks
+void CodeEdit::set_line_as_bookmarked(int p_line, bool p_bookmarked) {
+ int mask = get_line_gutter_metadata(p_line, main_gutter);
+ set_line_gutter_metadata(p_line, main_gutter, p_bookmarked ? mask | MAIN_GUTTER_BOOKMARK : mask & ~MAIN_GUTTER_BOOKMARK);
+}
+
+bool CodeEdit::is_line_bookmarked(int p_line) const {
+ return (int)get_line_gutter_metadata(p_line, main_gutter) & MAIN_GUTTER_BOOKMARK;
+}
+
+void CodeEdit::clear_bookmarked_lines() {
+ for (int i = 0; i < get_line_count(); i++) {
+ if (is_line_bookmarked(i)) {
+ set_line_as_bookmarked(i, false);
+ }
+ }
+}
+
+Array CodeEdit::get_bookmarked_lines() const {
+ Array ret;
+ for (int i = 0; i < get_line_count(); i++) {
+ if (is_line_bookmarked(i)) {
+ ret.append(i);
+ }
+ }
+ return ret;
+}
+
+// executing lines
+void CodeEdit::set_line_as_executing(int p_line, bool p_executing) {
+ int mask = get_line_gutter_metadata(p_line, main_gutter);
+ set_line_gutter_metadata(p_line, main_gutter, p_executing ? mask | MAIN_GUTTER_EXECUTING : mask & ~MAIN_GUTTER_EXECUTING);
+}
+
+bool CodeEdit::is_line_executing(int p_line) const {
+ return (int)get_line_gutter_metadata(p_line, main_gutter) & MAIN_GUTTER_EXECUTING;
+}
+
+void CodeEdit::clear_executing_lines() {
+ for (int i = 0; i < get_line_count(); i++) {
+ if (is_line_executing(i)) {
+ set_line_as_executing(i, false);
+ }
+ }
+}
+
+Array CodeEdit::get_executing_lines() const {
+ Array ret;
+ for (int i = 0; i < get_line_count(); i++) {
+ if (is_line_executing(i)) {
+ ret.append(i);
+ }
+ }
+ return ret;
+}
+
+/* Line numbers */
+void CodeEdit::set_draw_line_numbers(bool p_draw) {
+ set_gutter_draw(line_number_gutter, p_draw);
+}
+
+bool CodeEdit::is_draw_line_numbers_enabled() const {
+ return is_gutter_drawn(line_number_gutter);
+}
+
+void CodeEdit::set_line_numbers_zero_padded(bool p_zero_padded) {
+ p_zero_padded ? line_number_padding = "0" : line_number_padding = " ";
+ update();
+}
+
+bool CodeEdit::is_line_numbers_zero_padded() const {
+ return line_number_padding == "0";
+}
+
+void CodeEdit::_line_number_draw_callback(int p_line, int p_gutter, const Rect2 &p_region) {
+ String fc = String::num(p_line + 1).lpad(line_number_digits, line_number_padding);
+
+ int yofs = p_region.position.y + (cache.row_height - cache.font->get_height()) / 2;
+ Color number_color = get_line_gutter_item_color(p_line, line_number_gutter);
+ if (number_color == Color(1, 1, 1)) {
+ number_color = line_number_color;
+ }
+ cache.font->draw(get_canvas_item(), Point2(p_region.position.x, yofs + cache.font->get_ascent()), fc, number_color);
+}
+
+/* Fold Gutter */
+void CodeEdit::set_draw_fold_gutter(bool p_draw) {
+ set_gutter_draw(fold_gutter, p_draw);
+}
+
+bool CodeEdit::is_drawing_fold_gutter() const {
+ return is_gutter_drawn(fold_gutter);
+}
+
+void CodeEdit::_fold_gutter_draw_callback(int p_line, int p_gutter, Rect2 p_region) {
+ if (!can_fold(p_line) && !is_folded(p_line)) {
+ set_line_gutter_clickable(p_line, fold_gutter, false);
+ return;
+ }
+ set_line_gutter_clickable(p_line, fold_gutter, true);
+
+ int horizontal_padding = p_region.size.x / 10;
+ int vertical_padding = p_region.size.y / 6;
+
+ p_region.position += Point2(horizontal_padding, vertical_padding);
+ p_region.size -= Point2(horizontal_padding, vertical_padding) * 2;
+
+ if (can_fold(p_line)) {
+ can_fold_icon->draw_rect(get_canvas_item(), p_region, false, folding_color);
+ return;
+ }
+ folded_icon->draw_rect(get_canvas_item(), p_region, false, folding_color);
+}
+
+void CodeEdit::_bind_methods() {
+ /* Main Gutter */
+ ClassDB::bind_method(D_METHOD("_main_gutter_draw_callback"), &CodeEdit::_main_gutter_draw_callback);
+
+ ClassDB::bind_method(D_METHOD("set_draw_breakpoints_gutter", "enable"), &CodeEdit::set_draw_breakpoints_gutter);
+ ClassDB::bind_method(D_METHOD("is_drawing_breakpoints_gutter"), &CodeEdit::is_drawing_breakpoints_gutter);
+
+ ClassDB::bind_method(D_METHOD("set_draw_bookmarks_gutter", "enable"), &CodeEdit::set_draw_bookmarks_gutter);
+ ClassDB::bind_method(D_METHOD("is_drawing_bookmarks_gutter"), &CodeEdit::is_drawing_bookmarks_gutter);
+
+ ClassDB::bind_method(D_METHOD("set_draw_executing_lines_gutter", "enable"), &CodeEdit::set_draw_executing_lines_gutter);
+ ClassDB::bind_method(D_METHOD("is_drawing_executing_lines_gutter"), &CodeEdit::is_drawing_executing_lines_gutter);
+
+ // Breakpoints
+ ClassDB::bind_method(D_METHOD("set_line_as_breakpoint", "line", "breakpointed"), &CodeEdit::set_line_as_breakpoint);
+ ClassDB::bind_method(D_METHOD("is_line_breakpointed", "line"), &CodeEdit::is_line_breakpointed);
+ ClassDB::bind_method(D_METHOD("clear_breakpointed_lines"), &CodeEdit::clear_breakpointed_lines);
+ ClassDB::bind_method(D_METHOD("get_breakpointed_lines"), &CodeEdit::get_breakpointed_lines);
+
+ // Bookmarks
+ ClassDB::bind_method(D_METHOD("set_line_as_bookmarked", "line", "bookmarked"), &CodeEdit::set_line_as_bookmarked);
+ ClassDB::bind_method(D_METHOD("is_line_bookmarked", "line"), &CodeEdit::is_line_bookmarked);
+ ClassDB::bind_method(D_METHOD("clear_bookmarked_lines"), &CodeEdit::clear_bookmarked_lines);
+ ClassDB::bind_method(D_METHOD("get_bookmarked_lines"), &CodeEdit::get_bookmarked_lines);
+
+ // executing lines
+ ClassDB::bind_method(D_METHOD("set_line_as_executing", "line", "executing"), &CodeEdit::set_line_as_executing);
+ ClassDB::bind_method(D_METHOD("is_line_executing", "line"), &CodeEdit::is_line_executing);
+ ClassDB::bind_method(D_METHOD("clear_executing_lines"), &CodeEdit::clear_executing_lines);
+ ClassDB::bind_method(D_METHOD("get_executing_lines"), &CodeEdit::get_executing_lines);
+
+ /* Line numbers */
+ ClassDB::bind_method(D_METHOD("_line_number_draw_callback"), &CodeEdit::_line_number_draw_callback);
+
+ ClassDB::bind_method(D_METHOD("set_draw_line_numbers", "enable"), &CodeEdit::set_draw_line_numbers);
+ ClassDB::bind_method(D_METHOD("is_draw_line_numbers_enabled"), &CodeEdit::is_draw_line_numbers_enabled);
+ ClassDB::bind_method(D_METHOD("set_line_numbers_zero_padded", "enable"), &CodeEdit::set_line_numbers_zero_padded);
+ ClassDB::bind_method(D_METHOD("is_line_numbers_zero_padded"), &CodeEdit::is_line_numbers_zero_padded);
+
+ /* Fold Gutter */
+ ClassDB::bind_method(D_METHOD("_fold_gutter_draw_callback"), &CodeEdit::_fold_gutter_draw_callback);
+
+ ClassDB::bind_method(D_METHOD("set_draw_fold_gutter", "enable"), &CodeEdit::set_draw_fold_gutter);
+ ClassDB::bind_method(D_METHOD("is_drawing_fold_gutter"), &CodeEdit::is_drawing_fold_gutter);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_breakpoints_gutter"), "set_draw_breakpoints_gutter", "is_drawing_breakpoints_gutter");
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_bookmarks"), "set_draw_bookmarks_gutter", "is_drawing_bookmarks_gutter");
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_executing_lines"), "set_draw_executing_lines_gutter", "is_drawing_executing_lines_gutter");
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_line_numbers"), "set_draw_line_numbers", "is_draw_line_numbers_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "zero_pad_line_numbers"), "set_line_numbers_zero_padded", "is_line_numbers_zero_padded");
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_fold_gutter"), "set_draw_fold_gutter", "is_drawing_fold_gutter");
+
+ ADD_SIGNAL(MethodInfo("breakpoint_toggled", PropertyInfo(Variant::INT, "line")));
+}
+
+void CodeEdit::_gutter_clicked(int p_line, int p_gutter) {
+ if (p_gutter == main_gutter) {
+ if (draw_breakpoints) {
+ set_line_as_breakpoint(p_line, !is_line_breakpointed(p_line));
+ }
+ return;
+ }
+
+ if (p_gutter == line_number_gutter) {
+ cursor_set_line(p_line);
+ return;
+ }
+
+ if (p_gutter == fold_gutter) {
+ if (is_folded(p_line)) {
+ unfold_line(p_line);
+ } else if (can_fold(p_line)) {
+ fold_line(p_line);
+ }
+ return;
+ }
+}
+
+void CodeEdit::_lines_edited_from(int p_from_line, int p_to_line) {
+ if (p_from_line == p_to_line) {
+ return;
+ }
+
+ int lc = get_line_count();
+ line_number_digits = 1;
+ while (lc /= 10) {
+ line_number_digits++;
+ }
+ set_gutter_width(line_number_gutter, (line_number_digits + 1) * cache.font->get_char_size('0').width);
+
+ int from_line = MIN(p_from_line, p_to_line);
+ int line_count = (p_to_line - p_from_line);
+ List<int> breakpoints;
+ breakpointed_lines.get_key_list(&breakpoints);
+ for (const List<int>::Element *E = breakpoints.front(); E; E = E->next()) {
+ int line = E->get();
+ if (line <= from_line) {
+ continue;
+ }
+ breakpointed_lines.erase(line);
+
+ emit_signal("breakpoint_toggled", line);
+ if (line_count > 0 || line >= p_from_line) {
+ emit_signal("breakpoint_toggled", line + line_count);
+ breakpointed_lines[line + line_count] = true;
+ continue;
+ }
+ }
+}
+
+void CodeEdit::_update_gutter_indexes() {
+ for (int i = 0; i < get_gutter_count(); i++) {
+ if (get_gutter_name(i) == "main_gutter") {
+ main_gutter = i;
+ continue;
+ }
+
+ if (get_gutter_name(i) == "line_numbers") {
+ line_number_gutter = i;
+ continue;
+ }
+
+ if (get_gutter_name(i) == "fold_gutter") {
+ fold_gutter = i;
+ continue;
+ }
+ }
+}
+
+CodeEdit::CodeEdit() {
+ /* Gutters */
+ int gutter_idx = 0;
+
+ /* Main Gutter */
+ add_gutter();
+ set_gutter_name(gutter_idx, "main_gutter");
+ set_gutter_draw(gutter_idx, false);
+ set_gutter_overwritable(gutter_idx, true);
+ set_gutter_type(gutter_idx, GUTTER_TPYE_CUSTOM);
+ set_gutter_custom_draw(gutter_idx, this, "_main_gutter_draw_callback");
+ gutter_idx++;
+
+ /* Line numbers */
+ add_gutter();
+ set_gutter_name(gutter_idx, "line_numbers");
+ set_gutter_draw(gutter_idx, false);
+ set_gutter_type(gutter_idx, GUTTER_TPYE_CUSTOM);
+ set_gutter_custom_draw(gutter_idx, this, "_line_number_draw_callback");
+ gutter_idx++;
+
+ /* Fold Gutter */
+ add_gutter();
+ set_gutter_name(gutter_idx, "fold_gutter");
+ set_gutter_draw(gutter_idx, false);
+ set_gutter_type(gutter_idx, GUTTER_TPYE_CUSTOM);
+ set_gutter_custom_draw(gutter_idx, this, "_fold_gutter_draw_callback");
+ gutter_idx++;
+
+ connect("lines_edited_from", callable_mp(this, &CodeEdit::_lines_edited_from));
+ connect("gutter_clicked", callable_mp(this, &CodeEdit::_gutter_clicked));
+
+ connect("gutter_added", callable_mp(this, &CodeEdit::_update_gutter_indexes));
+ connect("gutter_removed", callable_mp(this, &CodeEdit::_update_gutter_indexes));
+ _update_gutter_indexes();
+}
+
+CodeEdit::~CodeEdit() {
+}
diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h
new file mode 100644
index 0000000000..c989e5ed79
--- /dev/null
+++ b/scene/gui/code_edit.h
@@ -0,0 +1,135 @@
+/*************************************************************************/
+/* code_edit.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 CODEEDIT_H
+#define CODEEDIT_H
+
+#include "scene/gui/text_edit.h"
+
+class CodeEdit : public TextEdit {
+ GDCLASS(CodeEdit, TextEdit)
+
+private:
+ /* Main Gutter */
+ enum MainGutterType {
+ MAIN_GUTTER_BREAKPOINT = 0x01,
+ MAIN_GUTTER_BOOKMARK = 0x02,
+ MAIN_GUTTER_EXECUTING = 0x04
+ };
+
+ int main_gutter = -1;
+ void _update_draw_main_gutter();
+ void _main_gutter_draw_callback(int p_line, int p_gutter, const Rect2 &p_region);
+
+ // breakpoints
+ HashMap<int, bool> breakpointed_lines;
+ bool draw_breakpoints = false;
+ Color breakpoint_color = Color(1, 1, 1);
+ Ref<Texture2D> breakpoint_icon = Ref<Texture2D>();
+
+ // bookmarks
+ bool draw_bookmarks = false;
+ Color bookmark_color = Color(1, 1, 1);
+ Ref<Texture2D> bookmark_icon = Ref<Texture2D>();
+
+ // executing lines
+ bool draw_executing_lines = false;
+ Color executing_line_color = Color(1, 1, 1);
+ Ref<Texture2D> executing_line_icon = Ref<Texture2D>();
+
+ /* Line numbers */
+ int line_number_gutter = -1;
+ int line_number_digits = 0;
+ String line_number_padding = " ";
+ Color line_number_color = Color(1, 1, 1);
+ void _line_number_draw_callback(int p_line, int p_gutter, const Rect2 &p_region);
+
+ /* Fold Gutter */
+ int fold_gutter = -1;
+ bool draw_fold_gutter = false;
+ Color folding_color = Color(1, 1, 1);
+ Ref<Texture2D> can_fold_icon = Ref<Texture2D>();
+ Ref<Texture2D> folded_icon = Ref<Texture2D>();
+ void _fold_gutter_draw_callback(int p_line, int p_gutter, Rect2 p_region);
+
+ void _gutter_clicked(int p_line, int p_gutter);
+ void _lines_edited_from(int p_from_line, int p_to_line);
+
+ void _update_gutter_indexes();
+
+protected:
+ void _notification(int p_what);
+
+ static void _bind_methods();
+
+public:
+ /* Main Gutter */
+ void set_draw_breakpoints_gutter(bool p_draw);
+ bool is_drawing_breakpoints_gutter() const;
+
+ void set_draw_bookmarks_gutter(bool p_draw);
+ bool is_drawing_bookmarks_gutter() const;
+
+ void set_draw_executing_lines_gutter(bool p_draw);
+ bool is_drawing_executing_lines_gutter() const;
+
+ // breakpoints
+ void set_line_as_breakpoint(int p_line, bool p_breakpointed);
+ bool is_line_breakpointed(int p_line) const;
+ void clear_breakpointed_lines();
+ Array get_breakpointed_lines() const;
+
+ // bookmarks
+ void set_line_as_bookmarked(int p_line, bool p_bookmarked);
+ bool is_line_bookmarked(int p_line) const;
+ void clear_bookmarked_lines();
+ Array get_bookmarked_lines() const;
+
+ // executing lines
+ void set_line_as_executing(int p_line, bool p_executing);
+ bool is_line_executing(int p_line) const;
+ void clear_executing_lines();
+ Array get_executing_lines() const;
+
+ /* Line numbers */
+ void set_draw_line_numbers(bool p_draw);
+ bool is_draw_line_numbers_enabled() const;
+ void set_line_numbers_zero_padded(bool p_zero_padded);
+ bool is_line_numbers_zero_padded() const;
+
+ /* Fold gutter */
+ void set_draw_fold_gutter(bool p_draw);
+ bool is_drawing_fold_gutter() const;
+
+ CodeEdit();
+ ~CodeEdit();
+};
+
+#endif // CODEEDIT_H
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index 88710289c7..cbe0367c7b 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -66,7 +66,7 @@ void ColorPicker::_notification(int p_what) {
} break;
case NOTIFICATION_PARENTED: {
for (int i = 0; i < 4; i++) {
- set_margin((Margin)i, get_theme_constant("margin"));
+ set_margin((Margin)i, get_margin((Margin)i) + get_theme_constant("margin"));
}
} break;
case NOTIFICATION_VISIBILITY_CHANGED: {
@@ -758,7 +758,8 @@ ColorPicker::ColorPicker() :
sample->set_h_size_flags(SIZE_EXPAND_FILL);
sample->connect("draw", callable_mp(this, &ColorPicker::_sample_draw));
- btn_pick = memnew(ToolButton);
+ btn_pick = memnew(Button);
+ btn_pick->set_flat(true);
hb_smpl->add_child(btn_pick);
btn_pick->set_toggle_mode(true);
btn_pick->set_tooltip(TTR("Pick a color from the editor window."));
@@ -872,6 +873,7 @@ void ColorPickerButton::_color_changed(const Color &p_color) {
void ColorPickerButton::_modal_closed() {
emit_signal("popup_closed");
+ set_pressed(false);
}
void ColorPickerButton::pressed() {
@@ -975,9 +977,8 @@ void ColorPickerButton::_update_picker() {
popup->add_child(picker);
add_child(popup);
picker->connect("color_changed", callable_mp(this, &ColorPickerButton::_color_changed));
- popup->connect("modal_closed", callable_mp(this, &ColorPickerButton::_modal_closed));
popup->connect("about_to_popup", callable_mp((BaseButton *)this, &BaseButton::set_pressed), varray(true));
- popup->connect("popup_hide", callable_mp((BaseButton *)this, &BaseButton::set_pressed), varray(false));
+ popup->connect("popup_hide", callable_mp(this, &ColorPickerButton::_modal_closed));
picker->set_pick_color(color);
picker->set_edit_alpha(edit_alpha);
emit_signal("picker_created");
diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h
index 31ae92f4e4..128664b49d 100644
--- a/scene/gui/color_picker.h
+++ b/scene/gui/color_picker.h
@@ -41,7 +41,6 @@
#include "scene/gui/slider.h"
#include "scene/gui/spin_box.h"
#include "scene/gui/texture_rect.h"
-#include "scene/gui/tool_button.h"
class ColorPicker : public BoxContainer {
GDCLASS(ColorPicker, BoxContainer);
@@ -57,7 +56,7 @@ private:
HSeparator *preset_separator;
Button *bt_add_preset;
List<Color> presets;
- ToolButton *btn_pick;
+ Button *btn_pick;
CheckButton *btn_hsv;
CheckButton *btn_raw;
HSlider *scroll[4];
@@ -148,7 +147,7 @@ class ColorPickerButton : public Button {
void _color_changed(const Color &p_color);
void _modal_closed();
- virtual void pressed();
+ virtual void pressed() override;
void _update_picker();
diff --git a/scene/gui/container.cpp b/scene/gui/container.cpp
index b01e210776..470a7db2dc 100644
--- a/scene/gui/container.cpp
+++ b/scene/gui/container.cpp
@@ -140,7 +140,7 @@ void Container::queue_sort() {
return;
}
- MessageQueue::get_singleton()->push_call(this, "_sort_children");
+ MessageQueue::get_singleton()->push_callable(callable_mp(this, &Container::_sort_children));
pending_sort = true;
}
@@ -177,8 +177,6 @@ String Container::get_configuration_warning() const {
}
void Container::_bind_methods() {
- ClassDB::bind_method(D_METHOD("_sort_children"), &Container::_sort_children);
-
ClassDB::bind_method(D_METHOD("queue_sort"), &Container::queue_sort);
ClassDB::bind_method(D_METHOD("fit_child_in_rect", "child", "rect"), &Container::fit_child_in_rect);
diff --git a/scene/gui/container.h b/scene/gui/container.h
index c8db5ee28f..b789bcf3b0 100644
--- a/scene/gui/container.h
+++ b/scene/gui/container.h
@@ -42,9 +42,9 @@ class Container : public Control {
protected:
void queue_sort();
- virtual void add_child_notify(Node *p_child);
- virtual void move_child_notify(Node *p_child);
- virtual void remove_child_notify(Node *p_child);
+ virtual void add_child_notify(Node *p_child) override;
+ virtual void move_child_notify(Node *p_child) override;
+ virtual void remove_child_notify(Node *p_child) override;
void _notification(int p_what);
static void _bind_methods();
@@ -56,7 +56,7 @@ public:
void fit_child_in_rect(Control *p_child, const Rect2 &p_rect);
- virtual String get_configuration_warning() const;
+ virtual String get_configuration_warning() const override;
Container();
};
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 3b6df5d2b6..8d1dd5afeb 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -30,6 +30,7 @@
#include "control.h"
+#include "core/math/geometry_2d.h"
#include "core/message_queue.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
@@ -590,7 +591,7 @@ void Control::_notification(int p_notification) {
case NOTIFICATION_VISIBILITY_CHANGED: {
if (!is_visible_in_tree()) {
if (get_viewport() != nullptr) {
- get_viewport()->_gui_hid_control(this);
+ get_viewport()->_gui_hide_control(this);
}
//remove key focus
@@ -1172,7 +1173,17 @@ Rect2 Control::get_parent_anchorable_rect() const {
if (data.parent_canvas_item) {
parent_rect = data.parent_canvas_item->get_anchorable_rect();
} else {
+#ifdef TOOLS_ENABLED
+ Node *edited_root = get_tree()->get_edited_scene_root();
+ if (edited_root && (this == edited_root || edited_root->is_a_parent_of(this))) {
+ parent_rect.size = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height"));
+ } else {
+ parent_rect = get_viewport()->get_visible_rect();
+ }
+
+#else
parent_rect = get_viewport()->get_visible_rect();
+#endif
}
return parent_rect;
@@ -2293,8 +2304,8 @@ void Control::_window_find_focus_neighbour(const Vector2 &p_dir, Node *p_at, con
Vector2 fb = points[(j + 1) % 4];
Vector2 pa, pb;
- float d = Geometry::get_closest_points_between_segments(la, lb, fa, fb, pa, pb);
- //float d = Geometry::get_closest_distance_between_segments(Vector3(la.x,la.y,0),Vector3(lb.x,lb.y,0),Vector3(fa.x,fa.y,0),Vector3(fb.x,fb.y,0));
+ float d = Geometry2D::get_closest_points_between_segments(la, lb, fa, fb, pa, pb);
+ //float d = Geometry2D::get_closest_distance_between_segments(Vector3(la.x,la.y,0),Vector3(lb.x,lb.y,0),Vector3(fa.x,fa.y,0),Vector3(fb.x,fb.y,0));
if (d < r_closest_dist) {
r_closest_dist = d;
*r_closest = c;
diff --git a/scene/gui/control.h b/scene/gui/control.h
index 10d6ad168f..15e10df0c6 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -255,8 +255,8 @@ private:
static bool has_constants(Control *p_theme_owner, Window *p_theme_owner_window, const StringName &p_name, const StringName &p_type = StringName());
protected:
- virtual void add_child_notify(Node *p_child);
- virtual void remove_child_notify(Node *p_child);
+ virtual void add_child_notify(Node *p_child) override;
+ virtual void remove_child_notify(Node *p_child) override;
//virtual void _window_gui_input(InputEvent p_event);
@@ -288,28 +288,28 @@ public:
/* EDITOR */
#ifdef TOOLS_ENABLED
- virtual Dictionary _edit_get_state() const;
- virtual void _edit_set_state(const Dictionary &p_state);
+ virtual Dictionary _edit_get_state() const override;
+ virtual void _edit_set_state(const Dictionary &p_state) override;
- virtual void _edit_set_position(const Point2 &p_position);
- virtual Point2 _edit_get_position() const;
+ virtual void _edit_set_position(const Point2 &p_position) override;
+ virtual Point2 _edit_get_position() const override;
- virtual void _edit_set_scale(const Size2 &p_scale);
- virtual Size2 _edit_get_scale() const;
+ virtual void _edit_set_scale(const Size2 &p_scale) override;
+ virtual Size2 _edit_get_scale() const override;
- virtual void _edit_set_rect(const Rect2 &p_edit_rect);
- virtual Rect2 _edit_get_rect() const;
- virtual bool _edit_use_rect() const;
+ virtual void _edit_set_rect(const Rect2 &p_edit_rect) override;
+ virtual Rect2 _edit_get_rect() const override;
+ virtual bool _edit_use_rect() const override;
- virtual void _edit_set_rotation(float p_rotation);
- virtual float _edit_get_rotation() const;
- virtual bool _edit_use_rotation() const;
+ virtual void _edit_set_rotation(float p_rotation) override;
+ virtual float _edit_get_rotation() const override;
+ virtual bool _edit_use_rotation() const override;
- virtual void _edit_set_pivot(const Point2 &p_pivot);
- virtual Point2 _edit_get_pivot() const;
- virtual bool _edit_use_pivot() const;
+ virtual void _edit_set_pivot(const Point2 &p_pivot) override;
+ virtual Point2 _edit_get_pivot() const override;
+ virtual bool _edit_use_pivot() const override;
- virtual Size2 _edit_get_minimum_size() const;
+ virtual Size2 _edit_get_minimum_size() const override;
#endif
void accept_event();
@@ -363,7 +363,7 @@ public:
Rect2 get_global_rect() const;
Rect2 get_screen_rect() const;
Rect2 get_window_rect() const; ///< use with care, as it blocks waiting for the visual server
- Rect2 get_anchorable_rect() const;
+ Rect2 get_anchorable_rect() const override;
void set_rotation(float p_radians);
void set_rotation_degrees(float p_degrees);
@@ -462,7 +462,7 @@ public:
CursorShape get_default_cursor_shape() const;
virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const;
- virtual Transform2D get_transform() const;
+ virtual Transform2D get_transform() const override;
bool is_toplevel_control() const;
@@ -486,8 +486,8 @@ public:
void set_disable_visibility_clip(bool p_ignore);
bool is_visibility_clip_disabled() const;
- virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const;
- virtual String get_configuration_warning() const;
+ virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
+ virtual String get_configuration_warning() const override;
Control();
~Control();
diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp
index c6897fc684..9077bfa4ba 100644
--- a/scene/gui/dialogs.cpp
+++ b/scene/gui/dialogs.cpp
@@ -51,7 +51,9 @@ void AcceptDialog::_input_from_window(const Ref<InputEvent> &p_event) {
}
void AcceptDialog::_parent_focused() {
- _cancel_pressed();
+ if (!is_exclusive()) {
+ _cancel_pressed();
+ }
}
void AcceptDialog::_notification(int p_what) {
@@ -256,7 +258,7 @@ Button *AcceptDialog::add_cancel(const String &p_cancel) {
if (p_cancel == "") {
c = RTR("Cancel");
}
- Button *b = swap_ok_cancel ? add_button(c, true) : add_button(c);
+ Button *b = swap_cancel_ok ? add_button(c, true) : add_button(c);
b->connect("pressed", callable_mp(this, &AcceptDialog::_cancel_pressed));
return b;
}
@@ -284,9 +286,9 @@ void AcceptDialog::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dialog_autowrap"), "set_autowrap", "has_autowrap");
}
-bool AcceptDialog::swap_ok_cancel = false;
-void AcceptDialog::set_swap_ok_cancel(bool p_swap) {
- swap_ok_cancel = p_swap;
+bool AcceptDialog::swap_cancel_ok = false;
+void AcceptDialog::set_swap_cancel_ok(bool p_swap) {
+ swap_cancel_ok = p_swap;
}
AcceptDialog::AcceptDialog() {
@@ -295,6 +297,8 @@ AcceptDialog::AcceptDialog() {
set_wrap_controls(true);
set_visible(false);
set_transient(true);
+ set_exclusive(true);
+ set_clamp_to_embedder(true);
bg = memnew(Panel);
add_child(bg);
diff --git a/scene/gui/dialogs.h b/scene/gui/dialogs.h
index 5d7b6272bf..de08685ce2 100644
--- a/scene/gui/dialogs.h
+++ b/scene/gui/dialogs.h
@@ -54,13 +54,13 @@ class AcceptDialog : public Window {
void _custom_action(const String &p_action);
void _update_child_rects();
- static bool swap_ok_cancel;
+ static bool swap_cancel_ok;
void _input_from_window(const Ref<InputEvent> &p_event);
void _parent_focused();
protected:
- virtual Size2 _get_contents_minimum_size() const;
+ virtual Size2 _get_contents_minimum_size() const override;
void _notification(int p_what);
static void _bind_methods();
@@ -75,7 +75,7 @@ protected:
public:
Label *get_label() { return label; }
- static void set_swap_ok_cancel(bool p_swap);
+ static void set_swap_cancel_ok(bool p_swap);
void register_text_enter(Node *p_line_edit);
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index be6b542ae1..4aea2928f4 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -40,14 +40,18 @@ FileDialog::GetIconFunc FileDialog::get_large_icon_func = nullptr;
FileDialog::RegisterFunc FileDialog::register_func = nullptr;
FileDialog::RegisterFunc FileDialog::unregister_func = nullptr;
+void FileDialog::popup_file_dialog() {
+ popup_centered_clamped(Size2i(700, 500), 0.8f);
+}
+
VBoxContainer *FileDialog::get_vbox() {
return vbox;
}
void FileDialog::_theme_changed() {
- Color font_color = vbox->get_theme_color("font_color", "ToolButton");
- Color font_color_hover = vbox->get_theme_color("font_color_hover", "ToolButton");
- Color font_color_pressed = vbox->get_theme_color("font_color_pressed", "ToolButton");
+ Color font_color = vbox->get_theme_color("font_color", "Button");
+ Color font_color_hover = vbox->get_theme_color("font_color_hover", "Button");
+ Color font_color_pressed = vbox->get_theme_color("font_color_pressed", "Button");
dir_up->add_theme_color_override("icon_color_normal", font_color);
dir_up->add_theme_color_override("icon_color_hover", font_color_hover);
@@ -268,7 +272,7 @@ void FileDialog::_action_pressed() {
}
if (dir_access->file_exists(f)) {
- confirm_save->set_text(RTR("File Exists, Overwrite?"));
+ confirm_save->set_text(RTR("File exists, overwrite?"));
confirm_save->popup_centered(Size2(200, 80));
} else {
emit_signal("file_selected", f);
@@ -402,7 +406,9 @@ void FileDialog::update_file_list() {
TreeItem *root = tree->create_item();
Ref<Texture2D> folder = vbox->get_theme_icon("folder", "FileDialog");
+ Ref<Texture2D> file_icon = vbox->get_theme_icon("file", "FileDialog");
const Color folder_color = vbox->get_theme_color("folder_icon_modulate", "FileDialog");
+ const Color file_color = vbox->get_theme_color("file_icon_modulate", "FileDialog");
List<String> files;
List<String> dirs;
@@ -491,7 +497,10 @@ void FileDialog::update_file_list() {
if (get_icon_func) {
Ref<Texture2D> icon = get_icon_func(base_dir.plus_file(files.front()->get()));
ti->set_icon(0, icon);
+ } else {
+ ti->set_icon(0, file_icon);
}
+ ti->set_icon_modulate(0, file_color);
if (mode == FILE_MODE_OPEN_DIR) {
ti->set_custom_color(0, vbox->get_theme_color("files_disabled", "FileDialog"));
@@ -599,7 +608,7 @@ void FileDialog::set_current_file(const String &p_file) {
file->set_text(p_file);
update_dir();
invalidate();
- int lp = p_file.find_last(".");
+ int lp = p_file.rfind(".");
if (lp != -1) {
file->select(0, lp);
if (file->is_inside_tree() && !get_tree()->is_node_being_edited(file)) {
@@ -612,7 +621,7 @@ void FileDialog::set_current_path(const String &p_path) {
if (!p_path.size()) {
return;
}
- int pos = MAX(p_path.find_last("/"), p_path.find_last("\\"));
+ int pos = MAX(p_path.rfind("/"), p_path.rfind("\\"));
if (pos == -1) {
set_current_file(p_path);
} else {
@@ -854,7 +863,8 @@ FileDialog::FileDialog() {
HBoxContainer *hbc = memnew(HBoxContainer);
- dir_up = memnew(ToolButton);
+ dir_up = memnew(Button);
+ dir_up->set_flat(true);
dir_up->set_tooltip(RTR("Go to parent folder."));
hbc->add_child(dir_up);
dir_up->connect("pressed", callable_mp(this, &FileDialog::_go_up));
@@ -872,12 +882,14 @@ FileDialog::FileDialog() {
hbc->add_child(dir);
dir->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- refresh = memnew(ToolButton);
+ refresh = memnew(Button);
+ refresh->set_flat(true);
refresh->set_tooltip(RTR("Refresh files."));
refresh->connect("pressed", callable_mp(this, &FileDialog::update_file_list));
hbc->add_child(refresh);
- show_hidden = memnew(ToolButton);
+ show_hidden = memnew(Button);
+ show_hidden->set_flat(true);
show_hidden->set_toggle_mode(true);
show_hidden->set_pressed(is_showing_hidden_files());
show_hidden->set_tooltip(RTR("Toggle the visibility of hidden files."));
diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h
index 8bc536d576..8003650668 100644
--- a/scene/gui/file_dialog.h
+++ b/scene/gui/file_dialog.h
@@ -36,7 +36,6 @@
#include "scene/gui/dialogs.h"
#include "scene/gui/line_edit.h"
#include "scene/gui/option_button.h"
-#include "scene/gui/tool_button.h"
#include "scene/gui/tree.h"
class FileDialog : public ConfirmationDialog {
@@ -87,10 +86,10 @@ private:
DirAccess *dir_access;
ConfirmationDialog *confirm_save;
- ToolButton *dir_up;
+ Button *dir_up;
- ToolButton *refresh;
- ToolButton *show_hidden;
+ Button *refresh;
+ Button *show_hidden;
Vector<String> filters;
@@ -127,7 +126,7 @@ private:
bool _is_open_should_be_disabled();
- virtual void _post_popup();
+ virtual void _post_popup() override;
protected:
void _theme_changed();
@@ -136,6 +135,7 @@ protected:
static void _bind_methods();
//bind helpers
public:
+ void popup_file_dialog();
void clear_filters();
void add_filter(const String &p_filter);
void set_filters(const Vector<String> &p_filters);
diff --git a/scene/gui/gradient_edit.h b/scene/gui/gradient_edit.h
index 376837b66c..6e950703bb 100644
--- a/scene/gui/gradient_edit.h
+++ b/scene/gui/gradient_edit.h
@@ -64,7 +64,7 @@ public:
Vector<Color> get_colors() const;
void set_points(Vector<Gradient::Point> &p_points);
Vector<Gradient::Point> &get_points();
- virtual Size2 get_minimum_size() const;
+ virtual Size2 get_minimum_size() const override;
GradientEdit();
virtual ~GradientEdit();
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index 5489638125..467d252c81 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -33,6 +33,7 @@
#include "core/input/input.h"
#include "core/os/keyboard.h"
#include "scene/gui/box_container.h"
+#include "scene/gui/button.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_scale.h"
@@ -261,6 +262,7 @@ void GraphEdit::remove_child_notify(Node *p_child) {
if (gn) {
gn->disconnect("offset_changed", callable_mp(this, &GraphEdit::_graph_node_moved));
gn->disconnect("raise_request", callable_mp(this, &GraphEdit::_graph_node_raised));
+ gn->disconnect("item_rect_changed", callable_mp((CanvasItem *)connections_layer, &CanvasItem::update));
}
}
@@ -369,8 +371,9 @@ bool GraphEdit::_filter_input(const Point2 &p_point) {
void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
Ref<InputEventMouseButton> mb = p_ev;
if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT && mb->is_pressed()) {
+ connecting_valid = false;
Ref<Texture2D> port = get_theme_icon("port", "GraphNode");
- Vector2 mpos(mb->get_position().x, mb->get_position().y);
+ click_pos = mb->get_position();
for (int i = get_child_count() - 1; i >= 0; i--) {
GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
if (!gn) {
@@ -379,7 +382,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
for (int j = 0; j < gn->get_connection_output_count(); j++) {
Vector2 pos = gn->get_connection_output_position(j) + gn->get_position();
- if (is_in_hot_zone(pos, mpos)) {
+ if (is_in_hot_zone(pos, click_pos)) {
if (valid_left_disconnect_types.has(gn->get_connection_output_type(j))) {
//check disconnect
for (List<Connection>::Element *E = connections.front(); E; E = E->next()) {
@@ -421,7 +424,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
for (int j = 0; j < gn->get_connection_input_count(); j++) {
Vector2 pos = gn->get_connection_input_position(j) + gn->get_position();
- if (is_in_hot_zone(pos, mpos)) {
+ if (is_in_hot_zone(pos, click_pos)) {
if (right_disconnects || valid_right_disconnect_types.has(gn->get_connection_input_type(j))) {
//check disconnect
for (List<Connection>::Element *E = connections.front(); E; E = E->next()) {
@@ -469,37 +472,40 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
connecting_to = mm->get_position();
connecting_target = false;
top_layer->update();
+ connecting_valid = just_disconnected || click_pos.distance_to(connecting_to) > 20.0 * zoom;
- Ref<Texture2D> port = get_theme_icon("port", "GraphNode");
- Vector2 mpos = mm->get_position();
- for (int i = get_child_count() - 1; i >= 0; i--) {
- GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
- if (!gn) {
- continue;
- }
+ if (connecting_valid) {
+ Ref<Texture2D> port = get_theme_icon("port", "GraphNode");
+ Vector2 mpos = mm->get_position();
+ for (int i = get_child_count() - 1; i >= 0; i--) {
+ GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
+ if (!gn) {
+ continue;
+ }
- if (!connecting_out) {
- for (int j = 0; j < gn->get_connection_output_count(); j++) {
- Vector2 pos = gn->get_connection_output_position(j) + gn->get_position();
- int type = gn->get_connection_output_type(j);
- if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_hot_zone(pos, mpos)) {
- connecting_target = true;
- connecting_to = pos;
- connecting_target_to = gn->get_name();
- connecting_target_index = j;
- return;
+ if (!connecting_out) {
+ for (int j = 0; j < gn->get_connection_output_count(); j++) {
+ Vector2 pos = gn->get_connection_output_position(j) + gn->get_position();
+ int type = gn->get_connection_output_type(j);
+ if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_hot_zone(pos, mpos)) {
+ connecting_target = true;
+ connecting_to = pos;
+ connecting_target_to = gn->get_name();
+ connecting_target_index = j;
+ return;
+ }
}
- }
- } else {
- for (int j = 0; j < gn->get_connection_input_count(); j++) {
- Vector2 pos = gn->get_connection_input_position(j) + gn->get_position();
- int type = gn->get_connection_input_type(j);
- if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_hot_zone(pos, mpos)) {
- connecting_target = true;
- connecting_to = pos;
- connecting_target_to = gn->get_name();
- connecting_target_index = j;
- return;
+ } else {
+ for (int j = 0; j < gn->get_connection_input_count(); j++) {
+ Vector2 pos = gn->get_connection_input_position(j) + gn->get_position();
+ int type = gn->get_connection_input_type(j);
+ if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_hot_zone(pos, mpos)) {
+ connecting_target = true;
+ connecting_to = pos;
+ connecting_target_to = gn->get_name();
+ connecting_target_index = j;
+ return;
+ }
}
}
}
@@ -507,27 +513,29 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
}
if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT && !mb->is_pressed()) {
- if (connecting && connecting_target) {
- String from = connecting_from;
- int from_slot = connecting_index;
- String to = connecting_target_to;
- int to_slot = connecting_target_index;
-
- if (!connecting_out) {
- SWAP(from, to);
- SWAP(from_slot, to_slot);
- }
- emit_signal("connection_request", from, from_slot, to, to_slot);
+ if (connecting_valid) {
+ if (connecting && connecting_target) {
+ String from = connecting_from;
+ int from_slot = connecting_index;
+ String to = connecting_target_to;
+ int to_slot = connecting_target_index;
+
+ if (!connecting_out) {
+ SWAP(from, to);
+ SWAP(from_slot, to_slot);
+ }
+ emit_signal("connection_request", from, from_slot, to, to_slot);
- } else if (!just_disconnected) {
- String from = connecting_from;
- int from_slot = connecting_index;
- Vector2 ofs = Vector2(mb->get_position().x, mb->get_position().y);
+ } else if (!just_disconnected) {
+ String from = connecting_from;
+ int from_slot = connecting_index;
+ Vector2 ofs = Vector2(mb->get_position().x, mb->get_position().y);
- if (!connecting_out) {
- emit_signal("connection_from_empty", from, from_slot, ofs);
- } else {
- emit_signal("connection_to_empty", from, from_slot, ofs);
+ if (!connecting_out) {
+ emit_signal("connection_from_empty", from, from_slot, ofs);
+ } else {
+ emit_signal("connection_to_empty", from, from_slot, ofs);
+ }
}
}
@@ -768,9 +776,7 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
if (mm.is_valid() && dragging) {
just_selected = true;
- // TODO: Remove local mouse pos hack if/when InputEventMouseMotion is fixed to support floats
- //drag_accum+=Vector2(mm->get_relative().x,mm->get_relative().y);
- drag_accum = get_local_mouse_position() - drag_origin;
+ drag_accum += mm->get_relative();
for (int i = get_child_count() - 1; i >= 0; i--) {
GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
if (gn && gn->is_selected()) {
@@ -789,7 +795,7 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
}
if (mm.is_valid() && box_selecting) {
- box_selecting_to = get_local_mouse_position();
+ box_selecting_to = mm->get_position();
box_selecting_rect = Rect2(MIN(box_selecting_from.x, box_selecting_to.x),
MIN(box_selecting_from.y, box_selecting_to.y),
@@ -807,9 +813,20 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
bool in_box = r.intersects(box_selecting_rect);
if (in_box) {
+ if (!gn->is_selected() && box_selection_mode_additive) {
+ emit_signal("node_selected", gn);
+ } else if (gn->is_selected() && !box_selection_mode_additive) {
+ emit_signal("node_unselected", gn);
+ }
gn->set_selected(box_selection_mode_additive);
} else {
- gn->set_selected(previus_selected.find(gn) != nullptr);
+ bool select = (previus_selected.find(gn) != nullptr);
+ if (gn->is_selected() && !select) {
+ emit_signal("node_unselected", gn);
+ } else if (!gn->is_selected() && select) {
+ emit_signal("node_selected", gn);
+ }
+ gn->set_selected(select);
}
}
@@ -827,7 +844,13 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
continue;
}
- gn->set_selected(previus_selected.find(gn) != nullptr);
+ bool select = (previus_selected.find(gn) != nullptr);
+ if (gn->is_selected() && !select) {
+ emit_signal("node_unselected", gn);
+ } else if (!gn->is_selected() && select) {
+ emit_signal("node_selected", gn);
+ }
+ gn->set_selected(select);
}
top_layer->update();
} else {
@@ -849,7 +872,8 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
if (gn) {
Rect2 r = gn->get_rect();
r.size *= zoom;
- if (r.has_point(get_local_mouse_position())) {
+ if (r.has_point(b->get_position())) {
+ emit_signal("node_unselected", gn);
gn->set_selected(false);
}
}
@@ -887,7 +911,7 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
continue;
}
- if (gn_selected->has_point(gn_selected->get_local_mouse_position())) {
+ if (gn_selected->has_point(b->get_position() - gn_selected->get_position())) {
gn = gn_selected;
break;
}
@@ -901,7 +925,6 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
dragging = true;
drag_accum = Vector2();
- drag_origin = get_local_mouse_position();
just_selected = !gn->is_selected();
if (!gn->is_selected() && !Input::get_singleton()->is_key_pressed(KEY_CONTROL)) {
for (int i = 0; i < get_child_count(); i++) {
@@ -939,7 +962,7 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
}
box_selecting = true;
- box_selecting_from = get_local_mouse_position();
+ box_selecting_from = b->get_position();
if (b->get_control()) {
box_selection_mode_additive = true;
previus_selected.clear();
@@ -1313,25 +1336,29 @@ GraphEdit::GraphEdit() {
top_layer->add_child(zoom_hb);
zoom_hb->set_position(Vector2(10, 10));
- zoom_minus = memnew(ToolButton);
+ zoom_minus = memnew(Button);
+ zoom_minus->set_flat(true);
zoom_hb->add_child(zoom_minus);
zoom_minus->set_tooltip(RTR("Zoom Out"));
zoom_minus->connect("pressed", callable_mp(this, &GraphEdit::_zoom_minus));
zoom_minus->set_focus_mode(FOCUS_NONE);
- zoom_reset = memnew(ToolButton);
+ zoom_reset = memnew(Button);
+ zoom_reset->set_flat(true);
zoom_hb->add_child(zoom_reset);
zoom_reset->set_tooltip(RTR("Zoom Reset"));
zoom_reset->connect("pressed", callable_mp(this, &GraphEdit::_zoom_reset));
zoom_reset->set_focus_mode(FOCUS_NONE);
- zoom_plus = memnew(ToolButton);
+ zoom_plus = memnew(Button);
+ zoom_plus->set_flat(true);
zoom_hb->add_child(zoom_plus);
zoom_plus->set_tooltip(RTR("Zoom In"));
zoom_plus->connect("pressed", callable_mp(this, &GraphEdit::_zoom_plus));
zoom_plus->set_focus_mode(FOCUS_NONE);
- snap_button = memnew(ToolButton);
+ snap_button = memnew(Button);
+ snap_button->set_flat(true);
snap_button->set_toggle_mode(true);
snap_button->set_tooltip(RTR("Enable snap and show grid."));
snap_button->connect("pressed", callable_mp(this, &GraphEdit::_snap_toggled));
diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h
index 8cfc5d32b9..37cb5989e9 100644
--- a/scene/gui/graph_edit.h
+++ b/scene/gui/graph_edit.h
@@ -32,12 +32,12 @@
#define GRAPH_EDIT_H
#include "scene/gui/box_container.h"
+#include "scene/gui/button.h"
#include "scene/gui/graph_node.h"
#include "scene/gui/scroll_bar.h"
#include "scene/gui/slider.h"
#include "scene/gui/spin_box.h"
#include "scene/gui/texture_rect.h"
-#include "scene/gui/tool_button.h"
class GraphEdit;
@@ -46,7 +46,7 @@ class GraphEditFilter : public Control {
friend class GraphEdit;
GraphEdit *ge;
- virtual bool has_point(const Point2 &p_point) const;
+ virtual bool has_point(const Point2 &p_point) const override;
public:
GraphEditFilter(GraphEdit *p_edit);
@@ -65,11 +65,11 @@ public:
};
private:
- ToolButton *zoom_minus;
- ToolButton *zoom_reset;
- ToolButton *zoom_plus;
+ Button *zoom_minus;
+ Button *zoom_reset;
+ Button *zoom_plus;
- ToolButton *snap_button;
+ Button *snap_button;
SpinBox *snap_amount;
void _zoom_minus();
@@ -93,11 +93,12 @@ private:
String connecting_target_to;
int connecting_target_index;
bool just_disconnected;
+ bool connecting_valid;
+ Vector2 click_pos;
bool dragging;
bool just_selected;
Vector2 drag_accum;
- Point2 drag_origin; // Workaround for GH-5907
float zoom;
@@ -173,10 +174,10 @@ private:
protected:
static void _bind_methods();
- virtual void add_child_notify(Node *p_child);
- virtual void remove_child_notify(Node *p_child);
+ virtual void add_child_notify(Node *p_child) override;
+ virtual void remove_child_notify(Node *p_child) override;
void _notification(int p_what);
- virtual bool clips_input() const;
+ virtual bool clips_input() const override;
public:
Error connect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port);
diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp
index b6a96238dc..01b54ddaa8 100644
--- a/scene/gui/graph_node.cpp
+++ b/scene/gui/graph_node.cpp
@@ -503,9 +503,7 @@ void GraphNode::_connpos_update() {
}
}
- if (vofs > 0) {
- vofs += sep;
- }
+ vofs += sep;
vofs += size.y;
idx++;
}
diff --git a/scene/gui/graph_node.h b/scene/gui/graph_node.h
index 6372833e6f..0cf6d9b09a 100644
--- a/scene/gui/graph_node.h
+++ b/scene/gui/graph_node.h
@@ -109,7 +109,7 @@ protected:
void _get_property_list(List<PropertyInfo> *p_list) const;
public:
- bool has_point(const Point2 &p_point) const;
+ bool has_point(const Point2 &p_point) const override;
void set_slot(int p_idx, bool p_enable_left, int p_type_left, const Color &p_color_left, bool p_enable_right, int p_type_right, const Color &p_color_right, const Ref<Texture2D> &p_custom_left = Ref<Texture2D>(), const Ref<Texture2D> &p_custom_right = Ref<Texture2D>());
void clear_slot(int p_idx);
@@ -154,7 +154,7 @@ public:
void set_resizable(bool p_enable);
bool is_resizable() const;
- virtual Size2 get_minimum_size() const;
+ virtual Size2 get_minimum_size() const override;
bool is_resizing() const { return resizing; }
diff --git a/scene/gui/grid_container.h b/scene/gui/grid_container.h
index 0a1bd6751a..79d4aee284 100644
--- a/scene/gui/grid_container.h
+++ b/scene/gui/grid_container.h
@@ -45,7 +45,7 @@ protected:
public:
void set_columns(int p_columns);
int get_columns() const;
- virtual Size2 get_minimum_size() const;
+ virtual Size2 get_minimum_size() const override;
GridContainer();
};
diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h
index 4cdef40184..03f477940c 100644
--- a/scene/gui/item_list.h
+++ b/scene/gui/item_list.h
@@ -214,7 +214,7 @@ public:
void sort_items_by_text();
int find_metadata(const Variant &p_metadata) const;
- virtual String get_tooltip(const Point2 &p_pos) const;
+ virtual String get_tooltip(const Point2 &p_pos) const override;
int get_item_at_position(const Point2 &p_pos, bool p_exact = false) const;
bool is_pos_at_end_of_items(const Point2 &p_pos) const;
@@ -224,7 +224,7 @@ public:
void set_auto_height(bool p_enable);
bool has_auto_height() const;
- Size2 get_minimum_size() const;
+ Size2 get_minimum_size() const override;
void set_autoscroll_to_bottom(const bool p_enable);
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp
index f49acc1b96..9e3418a5c9 100644
--- a/scene/gui/label.cpp
+++ b/scene/gui/label.cpp
@@ -188,7 +188,7 @@ void Label::_notification(int p_what) {
int spaces = 0;
while (to && to->char_pos >= 0) {
taken += to->pixel_width;
- if (to != from && to->space_count) {
+ if (to->space_count) {
spaces += to->space_count;
}
to = to->next;
@@ -235,8 +235,8 @@ void Label::_notification(int p_what) {
float x_ofs_shadow = x_ofs;
for (int i = 0; i < from->word_len; i++) {
if (visible_chars < 0 || chars_total_shadow < visible_chars) {
- CharType c = xl_text[i + pos];
- CharType n = xl_text[i + pos + 1];
+ char32_t c = xl_text[i + pos];
+ char32_t n = xl_text[i + pos + 1];
if (uppercase) {
c = String::char_uppercase(c);
n = String::char_uppercase(n);
@@ -255,8 +255,8 @@ void Label::_notification(int p_what) {
}
for (int i = 0; i < from->word_len; i++) {
if (visible_chars < 0 || chars_total < visible_chars) {
- CharType c = xl_text[i + pos];
- CharType n = xl_text[i + pos + 1];
+ char32_t c = xl_text[i + pos];
+ char32_t n = xl_text[i + pos + 1];
if (uppercase) {
c = String::char_uppercase(c);
n = String::char_uppercase(n);
@@ -308,7 +308,7 @@ int Label::get_longest_line_width() const {
real_t line_width = 0;
for (int i = 0; i < xl_text.size(); i++) {
- CharType current = xl_text[i];
+ char32_t current = xl_text[i];
if (uppercase) {
current = String::char_uppercase(current);
}
@@ -390,7 +390,7 @@ void Label::regenerate_word_cache() {
WordCache *last = nullptr;
for (int i = 0; i <= xl_text.length(); i++) {
- CharType current = i < xl_text.length() ? xl_text[i] : L' '; //always a space at the end, so the algo works
+ char32_t current = i < xl_text.length() ? xl_text[i] : L' '; //always a space at the end, so the algo works
if (uppercase) {
current = String::char_uppercase(current);
@@ -420,6 +420,22 @@ void Label::regenerate_word_cache() {
wc->space_count = space_count;
current_word_size = 0;
space_count = 0;
+ } else if ((i == xl_text.length() || current == '\n') && last != nullptr && space_count != 0) {
+ //in case there are trailing white spaces we add a placeholder word cache with just the spaces
+ WordCache *wc = memnew(WordCache);
+ if (word_cache) {
+ last->next = wc;
+ } else {
+ word_cache = wc;
+ }
+ last = wc;
+
+ wc->pixel_width = 0;
+ wc->char_pos = 0;
+ wc->word_len = 0;
+ wc->space_count = space_count;
+ current_word_size = 0;
+ space_count = 0;
}
if (current == '\n') {
diff --git a/scene/gui/label.h b/scene/gui/label.h
index 670db69dce..510a716f5d 100644
--- a/scene/gui/label.h
+++ b/scene/gui/label.h
@@ -95,7 +95,7 @@ protected:
static void _bind_methods();
// bind helpers
public:
- virtual Size2 get_minimum_size() const;
+ virtual Size2 get_minimum_size() const override;
void set_align(Align p_align);
Align get_align() const;
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index e3c75491f7..1b8f04297d 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -42,7 +42,7 @@
#include "editor/editor_settings.h"
#endif
#include "scene/main/window.h"
-static bool _is_text_char(CharType c) {
+static bool _is_text_char(char32_t c) {
return !is_symbol(c);
}
@@ -51,7 +51,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
if (b.is_valid()) {
if (b->is_pressed() && b->get_button_index() == BUTTON_RIGHT && context_menu_enabled) {
- menu->set_position(get_global_transform().xform(get_local_mouse_position()));
+ menu->set_position(get_screen_transform().xform(get_local_mouse_position()));
menu->set_size(Vector2(1, 1));
//menu->set_scale(get_global_transform().get_scale());
menu->popup();
@@ -118,8 +118,12 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
selection.creating = false;
selection.doubleclick = false;
- if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD)) {
- DisplayServer::get_singleton()->virtual_keyboard_show(text, get_global_rect(), max_length);
+ if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) {
+ if (selection.enabled) {
+ DisplayServer::get_singleton()->virtual_keyboard_show(text, get_global_rect(), false, max_length, selection.begin, selection.end);
+ } else {
+ DisplayServer::get_singleton()->virtual_keyboard_show(text, get_global_rect(), false, max_length, cursor_pos);
+ }
}
}
@@ -270,6 +274,22 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
set_cursor_position(text.length());
shift_selection_check_post(k->get_shift());
} break;
+ case (KEY_BACKSPACE): {
+ if (!editable)
+ break;
+
+ // If selected, delete the selection
+ if (selection.enabled) {
+ selection_delete();
+ break;
+ }
+
+ // Otherwise delete from cursor to beginning of text edit
+ int current_pos = get_cursor_position();
+ if (current_pos != 0) {
+ delete_text(0, current_pos);
+ }
+ } break;
#endif
default: {
handled = false;
@@ -289,7 +309,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
case KEY_KP_ENTER:
case KEY_ENTER: {
emit_signal("text_entered", text);
- if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD)) {
+ if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) {
DisplayServer::get_singleton()->virtual_keyboard_hide();
}
@@ -557,11 +577,11 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
if (handled) {
accept_event();
- } else if (!k->get_command()) {
+ } else if (!k->get_command() || (k->get_command() && k->get_alt())) {
if (k->get_unicode() >= 32 && k->get_keycode() != KEY_DELETE) {
if (editable) {
selection_delete();
- CharType ucodestr[2] = { (CharType)k->get_unicode(), 0 };
+ char32_t ucodestr[2] = { (char32_t)k->get_unicode(), 0 };
int prev_len = text.length();
append_at_cursor(ucodestr);
if (text.length() != prev_len) {
@@ -668,18 +688,18 @@ void LineEdit::_notification(int p_what) {
update_placeholder_width();
update();
} break;
- case NOTIFICATION_WM_FOCUS_IN: {
+ case NOTIFICATION_WM_WINDOW_FOCUS_IN: {
window_has_focus = true;
draw_caret = true;
update();
} break;
- case NOTIFICATION_WM_FOCUS_OUT: {
+ case NOTIFICATION_WM_WINDOW_FOCUS_OUT: {
window_has_focus = false;
draw_caret = false;
update();
} break;
case NOTIFICATION_DRAW: {
- if ((!has_focus() && !menu->has_focus()) || !window_has_focus) {
+ if ((!has_focus() && !menu->has_focus() && !caret_force_displayed) || !window_has_focus) {
draw_caret = false;
}
@@ -786,8 +806,8 @@ void LineEdit::_notification(int p_what) {
break;
}
- CharType cchar = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs];
- CharType next = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs + 1];
+ char32_t cchar = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs];
+ char32_t next = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs + 1];
int im_char_width = font->get_char_size(cchar, next).width;
if ((x_ofs + im_char_width) > ofs_max) {
@@ -809,8 +829,8 @@ void LineEdit::_notification(int p_what) {
}
}
- CharType cchar = (pass && !text.empty()) ? secret_character[0] : t[char_ofs];
- CharType next = (pass && !text.empty()) ? secret_character[0] : t[char_ofs + 1];
+ char32_t cchar = (pass && !text.empty()) ? secret_character[0] : t[char_ofs];
+ char32_t 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.
@@ -849,8 +869,8 @@ void LineEdit::_notification(int p_what) {
break;
}
- CharType cchar = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs];
- CharType next = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs + 1];
+ char32_t cchar = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs];
+ char32_t next = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs + 1];
int im_char_width = font->get_char_size(cchar, next).width;
if ((x_ofs + im_char_width) > ofs_max) {
@@ -905,10 +925,14 @@ void LineEdit::_notification(int p_what) {
}
} break;
case NOTIFICATION_FOCUS_ENTER: {
- if (caret_blink_enabled) {
- caret_blink_timer->start();
- } else {
- draw_caret = true;
+ if (!caret_force_displayed) {
+ if (caret_blink_enabled) {
+ if (caret_blink_timer->is_stopped()) {
+ caret_blink_timer->start();
+ }
+ } else {
+ draw_caret = true;
+ }
}
if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID) {
@@ -917,13 +941,17 @@ void LineEdit::_notification(int p_what) {
DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + cursor_pos, get_viewport()->get_window_id());
}
- if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD)) {
- DisplayServer::get_singleton()->virtual_keyboard_show(text, get_global_rect(), max_length);
+ if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) {
+ if (selection.enabled) {
+ DisplayServer::get_singleton()->virtual_keyboard_show(text, get_global_rect(), false, max_length, selection.begin, selection.end);
+ } else {
+ DisplayServer::get_singleton()->virtual_keyboard_show(text, get_global_rect(), false, max_length, cursor_pos);
+ }
}
} break;
case NOTIFICATION_FOCUS_EXIT: {
- if (caret_blink_enabled) {
+ if (caret_blink_enabled && !caret_force_displayed) {
caret_blink_timer->stop();
}
@@ -934,7 +962,7 @@ void LineEdit::_notification(int p_what) {
ime_text = "";
ime_selection = Point2();
- if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD)) {
+ if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) {
DisplayServer::get_singleton()->virtual_keyboard_hide();
}
@@ -1143,9 +1171,11 @@ bool LineEdit::cursor_get_blink_enabled() const {
void LineEdit::cursor_set_blink_enabled(const bool p_enabled) {
caret_blink_enabled = p_enabled;
- if (has_focus()) {
+ if (has_focus() || caret_force_displayed) {
if (p_enabled) {
- caret_blink_timer->start();
+ if (caret_blink_timer->is_stopped()) {
+ caret_blink_timer->start();
+ }
} else {
caret_blink_timer->stop();
}
@@ -1154,6 +1184,16 @@ void LineEdit::cursor_set_blink_enabled(const bool p_enabled) {
draw_caret = true;
}
+bool LineEdit::cursor_get_force_displayed() const {
+ return caret_force_displayed;
+}
+
+void LineEdit::cursor_set_force_displayed(const bool p_enabled) {
+ caret_force_displayed = p_enabled;
+ cursor_set_blink_enabled(caret_blink_enabled);
+ update();
+}
+
float LineEdit::cursor_get_blink_speed() const {
return caret_blink_timer->get_wait_time();
}
@@ -1176,7 +1216,7 @@ void LineEdit::_reset_caret_blink_timer() {
void LineEdit::_toggle_draw_caret() {
draw_caret = !draw_caret;
- if (is_visible_in_tree() && has_focus() && window_has_focus) {
+ if (is_visible_in_tree() && ((has_focus() && window_has_focus) || caret_force_displayed)) {
update();
}
}
@@ -1203,6 +1243,8 @@ void LineEdit::delete_char() {
}
void LineEdit::delete_text(int p_from_column, int p_to_column) {
+ ERR_FAIL_COND_MSG(p_from_column < 0 || p_from_column > p_to_column || p_to_column > text.length(),
+ vformat("Positional parameters (from: %d, to: %d) are inverted or outside the text length (%d).", p_from_column, p_to_column, text.length()));
if (text.size() > 0) {
Ref<Font> font = get_theme_font("font");
if (font != nullptr) {
@@ -1632,6 +1674,14 @@ bool LineEdit::is_shortcut_keys_enabled() const {
return shortcut_keys_enabled;
}
+void LineEdit::set_virtual_keyboard_enabled(bool p_enable) {
+ virtual_keyboard_enabled = p_enable;
+}
+
+bool LineEdit::is_virtual_keyboard_enabled() const {
+ return virtual_keyboard_enabled;
+}
+
void LineEdit::set_selecting_enabled(bool p_enabled) {
selecting_enabled = p_enabled;
@@ -1770,11 +1820,15 @@ void LineEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_expand_to_text_length"), &LineEdit::get_expand_to_text_length);
ClassDB::bind_method(D_METHOD("cursor_set_blink_enabled", "enabled"), &LineEdit::cursor_set_blink_enabled);
ClassDB::bind_method(D_METHOD("cursor_get_blink_enabled"), &LineEdit::cursor_get_blink_enabled);
+ ClassDB::bind_method(D_METHOD("cursor_set_force_displayed", "enabled"), &LineEdit::cursor_set_force_displayed);
+ ClassDB::bind_method(D_METHOD("cursor_get_force_displayed"), &LineEdit::cursor_get_force_displayed);
ClassDB::bind_method(D_METHOD("cursor_set_blink_speed", "blink_speed"), &LineEdit::cursor_set_blink_speed);
ClassDB::bind_method(D_METHOD("cursor_get_blink_speed"), &LineEdit::cursor_get_blink_speed);
ClassDB::bind_method(D_METHOD("set_max_length", "chars"), &LineEdit::set_max_length);
ClassDB::bind_method(D_METHOD("get_max_length"), &LineEdit::get_max_length);
ClassDB::bind_method(D_METHOD("append_at_cursor", "text"), &LineEdit::append_at_cursor);
+ ClassDB::bind_method(D_METHOD("delete_char_at_cursor"), &LineEdit::delete_char);
+ ClassDB::bind_method(D_METHOD("delete_text", "from_column", "to_column"), &LineEdit::delete_text);
ClassDB::bind_method(D_METHOD("set_editable", "enabled"), &LineEdit::set_editable);
ClassDB::bind_method(D_METHOD("is_editable"), &LineEdit::is_editable);
ClassDB::bind_method(D_METHOD("set_secret", "enabled"), &LineEdit::set_secret);
@@ -1785,6 +1839,8 @@ void LineEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_menu"), &LineEdit::get_menu);
ClassDB::bind_method(D_METHOD("set_context_menu_enabled", "enable"), &LineEdit::set_context_menu_enabled);
ClassDB::bind_method(D_METHOD("is_context_menu_enabled"), &LineEdit::is_context_menu_enabled);
+ ClassDB::bind_method(D_METHOD("set_virtual_keyboard_enabled", "enable"), &LineEdit::set_virtual_keyboard_enabled);
+ ClassDB::bind_method(D_METHOD("is_virtual_keyboard_enabled"), &LineEdit::is_virtual_keyboard_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);
@@ -1820,6 +1876,7 @@ void LineEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING, "secret_character"), "set_secret_character", "get_secret_character");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand_to_text_length"), "set_expand_to_text_length", "get_expand_to_text_length");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "virtual_keyboard_enabled"), "set_virtual_keyboard_enabled", "is_virtual_keyboard_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clear_button_enabled"), "set_clear_button_enabled", "is_clear_button_enabled");
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");
@@ -1831,6 +1888,7 @@ void LineEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "cursor_set_blink_enabled", "cursor_get_blink_enabled");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "cursor_set_blink_speed", "cursor_get_blink_speed");
ADD_PROPERTY(PropertyInfo(Variant::INT, "caret_position"), "set_cursor_position", "get_cursor_position");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_force_displayed"), "cursor_set_force_displayed", "cursor_get_force_displayed");
}
LineEdit::LineEdit() {
@@ -1860,6 +1918,7 @@ LineEdit::LineEdit() {
draw_caret = true;
caret_blink_enabled = false;
+ caret_force_displayed = false;
caret_blink_timer = memnew(Timer);
add_child(caret_blink_timer);
caret_blink_timer->set_wait_time(0.65);
diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h
index d31a5cb8d8..d6cc1f1f11 100644
--- a/scene/gui/line_edit.h
+++ b/scene/gui/line_edit.h
@@ -90,6 +90,8 @@ private:
bool shortcut_keys_enabled;
+ bool virtual_keyboard_enabled = true;
+
Ref<Texture2D> right_icon;
struct Selection {
@@ -134,6 +136,7 @@ private:
void update_placeholder_width();
bool caret_blink_enabled;
+ bool caret_force_displayed;
bool draw_caret;
bool window_has_focus;
@@ -164,11 +167,11 @@ public:
void set_align(Align p_align);
Align get_align() const;
- virtual Variant get_drag_data(const Point2 &p_point);
- virtual bool can_drop_data(const Point2 &p_point, const Variant &p_data) const;
- virtual void drop_data(const Point2 &p_point, const Variant &p_data);
+ virtual Variant get_drag_data(const Point2 &p_point) override;
+ virtual bool can_drop_data(const Point2 &p_point, const Variant &p_data) const override;
+ virtual void drop_data(const Point2 &p_point, const Variant &p_data) override;
- virtual CursorShape get_cursor_shape(const Point2 &p_pos) const;
+ virtual CursorShape get_cursor_shape(const Point2 &p_pos) const override;
void menu_option(int p_option);
void set_context_menu_enabled(bool p_enable);
@@ -201,6 +204,9 @@ public:
float cursor_get_blink_speed() const;
void cursor_set_blink_speed(const float p_speed);
+ bool cursor_get_force_displayed() const;
+ void cursor_set_force_displayed(const bool p_enabled);
+
void copy_text();
void cut_text();
void paste_text();
@@ -216,7 +222,7 @@ public:
void set_secret_character(const String &p_string);
String get_secret_character() const;
- virtual Size2 get_minimum_size() const;
+ virtual Size2 get_minimum_size() const override;
void set_expand_to_text_length(bool p_enabled);
bool get_expand_to_text_length() const;
@@ -227,13 +233,16 @@ public:
void set_shortcut_keys_enabled(bool p_enabled);
bool is_shortcut_keys_enabled() const;
+ void set_virtual_keyboard_enabled(bool p_enable);
+ bool is_virtual_keyboard_enabled() const;
+
void set_selecting_enabled(bool p_enabled);
bool is_selecting_enabled() const;
void set_right_icon(const Ref<Texture2D> &p_icon);
Ref<Texture2D> get_right_icon();
- virtual bool is_text_field() const;
+ virtual bool is_text_field() const override;
LineEdit();
~LineEdit();
};
diff --git a/scene/gui/link_button.h b/scene/gui/link_button.h
index ee37a29f9d..b8469b529a 100644
--- a/scene/gui/link_button.h
+++ b/scene/gui/link_button.h
@@ -49,7 +49,7 @@ private:
UnderlineMode underline_mode;
protected:
- virtual Size2 get_minimum_size() const;
+ virtual Size2 get_minimum_size() const override;
void _notification(int p_what);
static void _bind_methods();
diff --git a/scene/gui/margin_container.h b/scene/gui/margin_container.h
index 2fa41ecb6b..12e230d9d7 100644
--- a/scene/gui/margin_container.h
+++ b/scene/gui/margin_container.h
@@ -40,7 +40,7 @@ protected:
void _notification(int p_what);
public:
- virtual Size2 get_minimum_size() const;
+ virtual Size2 get_minimum_size() const override;
MarginContainer();
};
diff --git a/scene/gui/menu_button.h b/scene/gui/menu_button.h
index 0cd161c1f0..6330899ad3 100644
--- a/scene/gui/menu_button.h
+++ b/scene/gui/menu_button.h
@@ -46,14 +46,14 @@ class MenuButton : public Button {
Array _get_items() const;
void _set_items(const Array &p_items);
- void _gui_input(Ref<InputEvent> p_event);
+ void _gui_input(Ref<InputEvent> p_event) override;
protected:
void _notification(int p_what);
static void _bind_methods();
public:
- virtual void pressed();
+ virtual void pressed() override;
PopupMenu *get_popup() const;
void set_switch_on_hover(bool p_enabled);
diff --git a/scene/gui/nine_patch_rect.h b/scene/gui/nine_patch_rect.h
index 23a40fb64b..487fe4c860 100644
--- a/scene/gui/nine_patch_rect.h
+++ b/scene/gui/nine_patch_rect.h
@@ -52,7 +52,7 @@ public:
protected:
void _notification(int p_what);
- virtual Size2 get_minimum_size() const;
+ virtual Size2 get_minimum_size() const override;
static void _bind_methods();
public:
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index e7de0f0912..5780cc5e71 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -334,9 +334,6 @@ OptionButton::OptionButton() {
popup = memnew(PopupMenu);
popup->hide();
add_child(popup);
- // popup->set_pass_on_modal_close_click(false);
- // popup->set_notify_transform(true);
- popup->set_allow_search(true);
popup->connect("index_pressed", callable_mp(this, &OptionButton::_selected));
popup->connect("id_focused", callable_mp(this, &OptionButton::_focused));
popup->connect("popup_hide", callable_mp((BaseButton *)this, &BaseButton::set_pressed), varray(false));
diff --git a/scene/gui/option_button.h b/scene/gui/option_button.h
index 69a94a34f3..fec7695969 100644
--- a/scene/gui/option_button.h
+++ b/scene/gui/option_button.h
@@ -48,10 +48,10 @@ class OptionButton : public Button {
Array _get_items() const;
void _set_items(const Array &p_items);
- virtual void pressed();
+ virtual void pressed() override;
protected:
- Size2 get_minimum_size() const;
+ Size2 get_minimum_size() const override;
void _notification(int p_what);
static void _bind_methods();
@@ -87,7 +87,7 @@ public:
PopupMenu *get_popup() const;
- virtual void get_translatable_strings(List<String> *p_strings) const;
+ virtual void get_translatable_strings(List<String> *p_strings) const override;
OptionButton();
~OptionButton();
diff --git a/scene/gui/panel_container.h b/scene/gui/panel_container.h
index b68bc223dc..92743f2c47 100644
--- a/scene/gui/panel_container.h
+++ b/scene/gui/panel_container.h
@@ -40,7 +40,7 @@ protected:
void _notification(int p_what);
public:
- virtual Size2 get_minimum_size() const;
+ virtual Size2 get_minimum_size() const override;
PanelContainer();
};
diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp
index 5fc5f9b669..8a65aa5032 100644
--- a/scene/gui/popup.cpp
+++ b/scene/gui/popup.cpp
@@ -41,55 +41,71 @@ void Popup::_input_from_window(const Ref<InputEvent> &p_event) {
}
}
-void Popup::_parent_focused() {
- _close_pressed();
+void Popup::_initialize_visible_parents() {
+ visible_parents.clear();
+
+ Window *parent_window = this;
+ while (parent_window) {
+ parent_window = parent_window->get_parent_visible_window();
+ if (parent_window) {
+ visible_parents.push_back(parent_window);
+ parent_window->connect("focus_entered", callable_mp(this, &Popup::_parent_focused));
+ parent_window->connect("tree_exited", callable_mp(this, &Popup::_deinitialize_visible_parents));
+ }
+ }
+}
+
+void Popup::_deinitialize_visible_parents() {
+ for (uint32_t i = 0; i < visible_parents.size(); ++i) {
+ visible_parents[i]->disconnect("focus_entered", callable_mp(this, &Popup::_parent_focused));
+ visible_parents[i]->disconnect("tree_exited", callable_mp(this, &Popup::_deinitialize_visible_parents));
+ }
+
+ visible_parents.clear();
}
void Popup::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_VISIBILITY_CHANGED: {
if (is_visible()) {
- parent_visible = get_parent_visible_window();
- if (parent_visible) {
- parent_visible->connect("focus_entered", callable_mp(this, &Popup::_parent_focused));
- }
+ _initialize_visible_parents();
} else {
- if (parent_visible) {
- parent_visible->disconnect("focus_entered", callable_mp(this, &Popup::_parent_focused));
- parent_visible = nullptr;
- }
-
+ _deinitialize_visible_parents();
emit_signal("popup_hide");
}
} break;
- case NOTIFICATION_EXIT_TREE: {
- if (parent_visible) {
- parent_visible->disconnect("focus_entered", callable_mp(this, &Popup::_parent_focused));
- parent_visible = nullptr;
+ case NOTIFICATION_WM_WINDOW_FOCUS_IN: {
+ if (has_focus()) {
+ popped_up = true;
}
} break;
+ case NOTIFICATION_EXIT_TREE: {
+ _deinitialize_visible_parents();
+ } break;
case NOTIFICATION_WM_CLOSE_REQUEST: {
_close_pressed();
-
+ } break;
+ case NOTIFICATION_APPLICATION_FOCUS_OUT: {
+ _close_pressed();
} break;
}
}
-void Popup::_close_pressed() {
- Window *parent_window = parent_visible;
- if (parent_visible) {
- parent_visible->disconnect("focus_entered", callable_mp(this, &Popup::_parent_focused));
- parent_visible = nullptr;
+void Popup::_parent_focused() {
+ if (popped_up) {
+ _close_pressed();
}
+}
+
+void Popup::_close_pressed() {
+ popped_up = false;
+
+ _deinitialize_visible_parents();
call_deferred("hide");
emit_signal("cancelled");
-
- if (parent_window) {
- //parent_window->grab_focus();
- }
}
void Popup::set_as_minsize() {
@@ -126,12 +142,32 @@ Rect2i Popup::_popup_adjust_rect() const {
current.position.y = parent.position.y;
}
+ if (current.size.y > parent.size.y) {
+ current.size.y = parent.size.y;
+ }
+
+ if (current.size.x > parent.size.x) {
+ current.size.x = parent.size.x;
+ }
+
+ // Early out if max size not set.
+ Size2i max_size = get_max_size();
+ if (max_size <= Size2()) {
+ return current;
+ }
+
+ if (current.size.x > max_size.x) {
+ current.size.x = max_size.x;
+ }
+
+ if (current.size.y > max_size.y) {
+ current.size.y = max_size.y;
+ }
+
return current;
}
Popup::Popup() {
- parent_visible = nullptr;
-
set_wrap_controls(true);
set_visible(false);
set_transient(true);
diff --git a/scene/gui/popup.h b/scene/gui/popup.h
index 0e32d55cb6..3e5b89ccf3 100644
--- a/scene/gui/popup.h
+++ b/scene/gui/popup.h
@@ -33,17 +33,24 @@
#include "scene/main/window.h"
+#include "core/local_vector.h"
+
class Popup : public Window {
GDCLASS(Popup, Window);
- Window *parent_visible;
+ LocalVector<Window *> visible_parents;
+ bool popped_up = false;
void _input_from_window(const Ref<InputEvent> &p_event);
+
+ void _initialize_visible_parents();
+ void _deinitialize_visible_parents();
+
void _parent_focused();
protected:
void _close_pressed();
- virtual Rect2i _popup_adjust_rect() const;
+ virtual Rect2i _popup_adjust_rect() const override;
void _notification(int p_what);
static void _bind_methods();
@@ -63,7 +70,7 @@ protected:
void _update_child_rects();
void _notification(int p_what);
- virtual Size2 _get_contents_minimum_size() const;
+ virtual Size2 _get_contents_minimum_size() const override;
public:
void set_child_rect(Control *p_child);
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index b439d85983..578d8a96e8 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -35,7 +35,6 @@
#include "core/os/os.h"
#include "core/print_string.h"
#include "core/translation.h"
-#include "scene/gui/control.h"
String PopupMenu::_get_accel_text(int p_item) const {
ERR_FAIL_INDEX_V(p_item, items.size(), String());
@@ -52,7 +51,8 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
int vseparation = get_theme_constant("vseparation");
int hseparation = get_theme_constant("hseparation");
- Size2 minsize = get_theme_stylebox("panel")->get_minimum_size();
+ Size2 minsize = get_theme_stylebox("panel")->get_minimum_size(); // Accounts for margin in the margin container
+ minsize.x += scroll_container->get_v_scrollbar()->get_size().width * 2; // Adds a buffer so that the scrollbar does not render over the top of content
Ref<Font> font = get_theme_font("font");
float max_w = 0;
@@ -64,13 +64,10 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
for (int i = 0; i < items.size(); i++) {
Size2 size;
- if (!items[i].icon.is_null()) {
- Size2 icon_size = items[i].icon->get_size();
- size.height = MAX(icon_size.height, font_h);
- icon_w = MAX(icon_size.width + hseparation, icon_w);
- } else {
- size.height = font_h;
- }
+
+ Size2 icon_size = items[i].get_icon_size();
+ size.height = MAX(icon_size.height, font_h);
+ icon_w = MAX(icon_size.width, icon_w);
size.width += items[i].h_ofs;
@@ -104,39 +101,71 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
minsize.width += check_w;
}
+ if (is_inside_tree()) {
+ int height_limit = get_usable_parent_rect().size.height;
+ if (minsize.height > height_limit) {
+ minsize.height = height_limit;
+ }
+ }
+
return minsize;
}
+int PopupMenu::_get_items_total_height() const {
+ int font_height = get_theme_font("font")->get_height();
+ int vsep = get_theme_constant("vseparation");
+
+ // Get total height of all items by taking max of icon height and font height
+ int items_total_height = 0;
+ for (int i = 0; i < items.size(); i++) {
+ items_total_height += MAX(items[i].get_icon_size().height, font_height) + vsep;
+ }
+
+ // Subtract a separator which is not needed for the last item.
+ return items_total_height - vsep;
+}
+
+void PopupMenu::_scroll_to_item(int p_item) {
+ ERR_FAIL_INDEX(p_item, items.size());
+ ERR_FAIL_COND(p_item < 0);
+
+ // Scroll item into view (upwards)
+ if (items[p_item]._ofs_cache < -control->get_position().y) {
+ int amnt_over = items[p_item]._ofs_cache + control->get_position().y;
+ scroll_container->set_v_scroll(scroll_container->get_v_scroll() + amnt_over);
+ }
+
+ // Scroll item into view (downwards)
+ if (items[p_item]._ofs_cache + items[p_item]._height_cache > -control->get_position().y + scroll_container->get_size().height) {
+ int amnt_over = items[p_item]._ofs_cache + items[p_item]._height_cache + control->get_position().y - scroll_container->get_size().height;
+ scroll_container->set_v_scroll(scroll_container->get_v_scroll() + amnt_over);
+ }
+}
+
int PopupMenu::_get_mouse_over(const Point2 &p_over) const {
if (p_over.x < 0 || p_over.x >= get_size().width) {
return -1;
}
- Ref<StyleBox> style = get_theme_stylebox("panel");
+ Ref<StyleBox> style = get_theme_stylebox("panel"); // Accounts for margin in the margin container
+
+ int vseparation = get_theme_constant("vseparation");
+ float font_h = get_theme_font("font")->get_height();
- Point2 ofs = style->get_offset();
+ Point2 ofs = style->get_offset() + Point2(0, vseparation / 2);
if (ofs.y > p_over.y) {
return -1;
}
- Ref<Font> font = get_theme_font("font");
- int vseparation = get_theme_constant("vseparation");
- float font_h = font->get_height();
-
for (int i = 0; i < items.size(); i++) {
- ofs.y += vseparation;
- float h;
-
- if (!items[i].icon.is_null()) {
- Size2 icon_size = items[i].icon->get_size();
- h = MAX(icon_size.height, font_h);
- } else {
- h = font_h;
+ if (i > 0) {
+ ofs.y += vseparation;
}
- ofs.y += h;
- if (p_over.y < ofs.y) {
+ ofs.y += MAX(items[i].get_icon_size().height, font_h);
+
+ if (p_over.y - control->get_position().y < ofs.y) {
return i;
}
}
@@ -147,43 +176,51 @@ int PopupMenu::_get_mouse_over(const Point2 &p_over) const {
void PopupMenu::_activate_submenu(int over) {
Node *n = get_node(items[over].submenu);
ERR_FAIL_COND_MSG(!n, "Item subnode does not exist: " + items[over].submenu + ".");
- Popup *pm = Object::cast_to<Popup>(n);
- ERR_FAIL_COND_MSG(!pm, "Item subnode is not a Popup: " + items[over].submenu + ".");
- if (pm->is_visible()) {
+ Popup *submenu_popup = Object::cast_to<Popup>(n);
+ ERR_FAIL_COND_MSG(!submenu_popup, "Item subnode is not a Popup: " + items[over].submenu + ".");
+ if (submenu_popup->is_visible()) {
return; //already visible!
}
- Point2 p = get_position();
- Rect2 pr(p, get_size());
Ref<StyleBox> style = get_theme_stylebox("panel");
+ int vsep = get_theme_constant("vseparation");
+
+ Point2 this_pos = get_position();
+ Rect2 this_rect(this_pos, get_size());
+
+ float scroll_offset = control->get_position().y;
- Point2 pos = p + Point2(get_size().width, items[over]._ofs_cache - style->get_offset().y);
- Size2 size = pm->get_size();
- // fix pos
- if (pos.x + size.width > get_parent_rect().size.width) {
- pos.x = p.x - size.width;
+ Point2 submenu_pos = this_pos + Point2(this_rect.size.width, items[over]._ofs_cache + scroll_offset);
+ Size2 submenu_size = submenu_popup->get_size();
+
+ // Fix pos if going outside parent rect
+ if (submenu_pos.x + submenu_size.width > get_parent_rect().size.width) {
+ submenu_pos.x = this_pos.x - submenu_size.width;
}
- pm->set_position(pos);
- // pm->set_scale(get_global_transform().get_scale());
- pm->popup();
-
- PopupMenu *pum = Object::cast_to<PopupMenu>(pm);
- if (pum) {
- pr.position -= pum->get_position();
- pum->clear_autohide_areas();
- pum->add_autohide_area(Rect2(pr.position.x, pr.position.y, pr.size.x, items[over]._ofs_cache));
- if (over < items.size() - 1) {
- int from = items[over + 1]._ofs_cache;
- pum->add_autohide_area(Rect2(pr.position.x, pr.position.y + from, pr.size.x, pr.size.y - from));
+ submenu_popup->set_position(submenu_pos);
+ submenu_popup->set_as_minsize(); // Shrink the popup size to it's contents.
+ submenu_popup->popup();
+
+ // Set autohide areas
+ PopupMenu *submenu_pum = Object::cast_to<PopupMenu>(submenu_popup);
+ if (submenu_pum) {
+ // Make the position of the parent popup relative to submenu popup
+ this_rect.position = this_rect.position - submenu_pum->get_position();
+
+ // Autohide area above the submenu item
+ submenu_pum->clear_autohide_areas();
+ submenu_pum->add_autohide_area(Rect2(this_rect.position.x, this_rect.position.y, this_rect.size.x, items[over]._ofs_cache + scroll_offset + style->get_offset().height - vsep / 2));
+
+ // If there is an area below the submenu item, add an autohide area there.
+ if (items[over]._ofs_cache + items[over]._height_cache + scroll_offset <= control->get_size().height) {
+ int from = items[over]._ofs_cache + items[over]._height_cache + scroll_offset + vsep / 2 + style->get_offset().height;
+ submenu_pum->add_autohide_area(Rect2(this_rect.position.x, this_rect.position.y + from, this_rect.size.x, this_rect.size.y - from));
}
}
}
void PopupMenu::_submenu_timeout() {
- //if (!has_focus()) {
- // return; //do not activate if not has focus
- //}
if (mouse_over == submenu_over) {
_activate_submenu(mouse_over);
}
@@ -191,70 +228,34 @@ void PopupMenu::_submenu_timeout() {
submenu_over = -1;
}
-void PopupMenu::_scroll(float p_factor, const Point2 &p_over) {
- int vseparation = get_theme_constant("vseparation");
- Ref<Font> font = get_theme_font("font");
-
- Rect2 visible_rect = get_usable_parent_rect();
-
- int dy = (vseparation + font->get_height()) * 3 * p_factor;
- if (dy > 0) {
- const float global_top = get_position().y;
- const float limit = global_top < visible_rect.position.y ? visible_rect.position.y - global_top : 0;
- dy = MIN(dy, limit);
- } else if (dy < 0) {
- const float global_bottom = get_position().y + get_size().y;
- const float viewport_height = visible_rect.position.y + visible_rect.size.y;
- const float limit = global_bottom > viewport_height ? global_bottom - viewport_height : 0;
- dy = -MIN(-dy, limit);
- }
-
- if (dy == 0) {
- return;
- }
-
- set_position(get_position() + Vector2(0, dy));
-
- Ref<InputEventMouseMotion> ie;
- ie.instance();
- ie->set_position(p_over - Vector2(0, dy));
- _gui_input(ie);
-}
-
void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
- if (p_event->is_action("ui_down") && p_event->is_pressed()) {
+ if (p_event->is_action("ui_down") && p_event->is_pressed() && mouse_over != items.size() - 1) {
int search_from = mouse_over + 1;
if (search_from >= items.size()) {
search_from = 0;
}
for (int i = search_from; i < items.size(); i++) {
- if (i < 0 || i >= items.size()) {
- continue;
- }
-
if (!items[i].separator && !items[i].disabled) {
mouse_over = i;
emit_signal("id_focused", i);
+ _scroll_to_item(i);
control->update();
set_input_as_handled();
break;
}
}
- } else if (p_event->is_action("ui_up") && p_event->is_pressed()) {
+ } else if (p_event->is_action("ui_up") && p_event->is_pressed() && mouse_over != 0) {
int search_from = mouse_over - 1;
if (search_from < 0) {
search_from = items.size() - 1;
}
for (int i = search_from; i >= 0; i--) {
- if (i >= items.size()) {
- continue;
- }
-
if (!items[i].separator && !items[i].disabled) {
mouse_over = i;
emit_signal("id_focused", i);
+ _scroll_to_item(i);
control->update();
set_input_as_handled();
break;
@@ -282,65 +283,61 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
}
}
+ // Make an area which does not include v scrollbar, so that items are not activated when dragging scrollbar.
+ Rect2 item_clickable_area = scroll_container->get_rect();
+ if (scroll_container->get_v_scrollbar()->is_visible_in_tree()) {
+ item_clickable_area.size.width -= scroll_container->get_v_scrollbar()->get_size().width;
+ }
+
Ref<InputEventMouseButton> b = p_event;
if (b.is_valid()) {
- if (b->is_pressed()) {
+ if (!item_clickable_area.has_point(b->get_position())) {
return;
}
int button_idx = b->get_button_index();
- switch (button_idx) {
- case BUTTON_WHEEL_DOWN: {
- _scroll(-b->get_factor(), b->get_position());
- } break;
- case BUTTON_WHEEL_UP: {
- _scroll(b->get_factor(), b->get_position());
- } break;
- default: {
- // Allow activating item by releasing the LMB or any that was down when the popup appeared
- if (button_idx == BUTTON_LEFT || (initial_button_mask & (1 << (button_idx - 1)))) {
- bool was_during_grabbed_click = during_grabbed_click;
- during_grabbed_click = false;
- initial_button_mask = 0;
-
- int over = _get_mouse_over(b->get_position());
-
- if (invalidated_click) {
- invalidated_click = false;
- break;
- }
- if (over < 0) {
- if (!was_during_grabbed_click) {
- hide();
- }
- break; //non-activable
- }
+ if (b->is_pressed() || (!b->is_pressed() && during_grabbed_click)) {
+ // Allow activating item by releasing the LMB or any that was down when the popup appeared.
+ // However, if button was not held when opening menu, do not allow release to activate item.
+ if (button_idx == BUTTON_LEFT || (initial_button_mask & (1 << (button_idx - 1)))) {
+ bool was_during_grabbed_click = during_grabbed_click;
+ during_grabbed_click = false;
+ initial_button_mask = 0;
+
+ // Disable clicks under a time threshold to avoid selection right when opening the popup.
+ uint64_t now = OS::get_singleton()->get_ticks_msec();
+ uint64_t diff = now - popup_time_msec;
+ if (diff < 100) {
+ return;
+ }
- if (items[over].separator || items[over].disabled) {
- break;
+ int over = _get_mouse_over(b->get_position());
+ if (over < 0) {
+ if (!was_during_grabbed_click) {
+ hide();
}
+ return;
+ }
- if (items[over].submenu != "") {
- _activate_submenu(over);
- return;
- }
- activate_item(over);
+ if (items[over].separator || items[over].disabled) {
+ return;
+ }
+
+ if (items[over].submenu != "") {
+ _activate_submenu(over);
+ return;
}
+ activate_item(over);
}
}
-
- //control->update();
}
Ref<InputEventMouseMotion> m = p_event;
if (m.is_valid()) {
- if (invalidated_click) {
- moved += m->get_relative();
- if (moved.length() > 4) {
- invalidated_click = false;
- }
+ if (!item_clickable_area.has_point(m->get_position())) {
+ return;
}
for (List<Rect2>::Element *E = autohide_areas.front(); E; E = E->next()) {
@@ -370,11 +367,6 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
}
}
- Ref<InputEventPanGesture> pan_gesture = p_event;
- if (pan_gesture.is_valid()) {
- _scroll(-pan_gesture->get_delta().y, pan_gesture->get_position());
- }
-
Ref<InputEventKey> k = p_event;
if (allow_search && k.is_valid() && k->get_unicode() && k->is_pressed()) {
@@ -407,6 +399,7 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
if (items[i].text.findn(search_string) == 0) {
mouse_over = i;
emit_signal("id_focused", i);
+ _scroll_to_item(i);
control->update();
set_input_as_handled();
break;
@@ -415,9 +408,13 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
}
}
-void PopupMenu::_draw() {
+void PopupMenu::_draw_items() {
+ control->set_custom_minimum_size(Size2(0, _get_items_total_height()));
RID ci = control->get_canvas_item();
- Size2 size = get_size();
+
+ Size2 margin_size;
+ margin_size.width = margin_container->get_theme_constant("margin_right") + margin_container->get_theme_constant("margin_left");
+ margin_size.height = margin_container->get_theme_constant("margin_top") + margin_container->get_theme_constant("margin_bottom");
Ref<StyleBox> style = get_theme_stylebox("panel");
Ref<StyleBox> hover = get_theme_stylebox("hover");
@@ -430,8 +427,6 @@ void PopupMenu::_draw() {
Ref<StyleBox> labeled_separator_left = get_theme_stylebox("labeled_separator_left");
Ref<StyleBox> labeled_separator_right = get_theme_stylebox("labeled_separator_right");
- style->draw(ci, Rect2(Point2(), get_size()));
- Point2 ofs = style->get_offset();
int vseparation = get_theme_constant("vseparation");
int hseparation = get_theme_constant("hseparation");
Color font_color = get_theme_color("font_color");
@@ -440,13 +435,14 @@ void PopupMenu::_draw() {
Color font_color_hover = get_theme_color("font_color_hover");
float font_h = font->get_height();
- // Add the check and the wider icon to the offset of all items.
+ float scroll_width = scroll_container->get_v_scrollbar()->is_visible_in_tree() ? scroll_container->get_v_scrollbar()->get_size().width : 0;
+ float display_width = control->get_size().width - scroll_width;
+
+ // Find the widest icon and whether any items have a checkbox, and store the offsets for each.
float icon_ofs = 0.0;
bool has_check = false;
for (int i = 0; i < items.size(); i++) {
- if (!items[i].icon.is_null()) {
- icon_ofs = MAX(items[i].icon->get_size().width, icon_ofs);
- }
+ icon_ofs = MAX(items[i].get_icon_size().width, icon_ofs);
if (items[i].checkable_type) {
has_check = true;
@@ -461,65 +457,68 @@ void PopupMenu::_draw() {
check_ofs = MAX(get_theme_icon("checked")->get_width(), get_theme_icon("radio_checked")->get_width()) + hseparation;
}
+ Point2 ofs = Point2();
+
+ // Loop through all items and draw each.
for (int i = 0; i < items.size(); i++) {
+ // If not the first item, add the separation space between items.
if (i > 0) {
ofs.y += vseparation;
}
- Point2 item_ofs = ofs;
- Size2 icon_size;
- float h;
- if (!items[i].icon.is_null()) {
- icon_size = items[i].icon->get_size();
- h = MAX(icon_size.height, font_h);
- } else {
- h = font_h;
- }
+ Point2 item_ofs = ofs;
+ Size2 icon_size = items[i].get_icon_size();
+ float h = MAX(icon_size.height, font_h);
if (i == mouse_over) {
- hover->draw(ci, Rect2(item_ofs + Point2(-hseparation, -vseparation / 2), Size2(get_size().width - style->get_minimum_size().width + hseparation * 2, h + vseparation)));
+ hover->draw(ci, Rect2(item_ofs + Point2(-hseparation, -vseparation / 2), Size2(display_width + hseparation * 2, h + vseparation)));
}
String text = items[i].xl_text;
+ // Separator
item_ofs.x += items[i].h_ofs;
if (items[i].separator) {
int sep_h = separator->get_center_size().height + separator->get_minimum_size().height;
if (text != String()) {
- int ss = font->get_string_size(text).width;
- int center = (get_size().width) / 2;
- int l = center - ss / 2;
- int r = center + ss / 2;
- if (l > item_ofs.x) {
- labeled_separator_left->draw(ci, Rect2(item_ofs + Point2(0, Math::floor((h - sep_h) / 2.0)), Size2(MAX(0, l - item_ofs.x), sep_h)));
+ int text_size = font->get_string_size(text).width;
+ int text_center = display_width / 2;
+ int text_left = text_center - text_size / 2;
+ int text_right = text_center + text_size / 2;
+ if (text_left > item_ofs.x) {
+ labeled_separator_left->draw(ci, Rect2(item_ofs + Point2(0, Math::floor((h - sep_h) / 2.0)), Size2(MAX(0, text_left - item_ofs.x), sep_h)));
}
- if (r < get_size().width - style->get_margin(MARGIN_RIGHT)) {
- labeled_separator_right->draw(ci, Rect2(Point2(r, item_ofs.y + Math::floor((h - sep_h) / 2.0)), Size2(MAX(0, get_size().width - style->get_margin(MARGIN_RIGHT) - r), sep_h)));
+ if (text_right < display_width) {
+ labeled_separator_right->draw(ci, Rect2(Point2(text_right, item_ofs.y + Math::floor((h - sep_h) / 2.0)), Size2(MAX(0, display_width - text_right), sep_h)));
}
} else {
- separator->draw(ci, Rect2(item_ofs + Point2(0, Math::floor((h - sep_h) / 2.0)), Size2(get_size().width - style->get_minimum_size().width, sep_h)));
+ separator->draw(ci, Rect2(item_ofs + Point2(0, Math::floor((h - sep_h) / 2.0)), Size2(display_width, sep_h)));
}
}
Color icon_color(1, 1, 1, items[i].disabled ? 0.5 : 1);
+ // Checkboxes
if (items[i].checkable_type) {
Texture2D *icon = (items[i].checked ? check[items[i].checkable_type - 1] : uncheck[items[i].checkable_type - 1]).ptr();
icon->draw(ci, item_ofs + Point2(0, Math::floor((h - icon->get_height()) / 2.0)), icon_color);
}
+ // Icon
if (!items[i].icon.is_null()) {
items[i].icon->draw(ci, item_ofs + Size2(check_ofs, 0) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color);
}
+ // Submenu arrow on right hand side
if (items[i].submenu != "") {
- submenu->draw(ci, Point2(size.width - style->get_margin(MARGIN_RIGHT) - submenu->get_width(), item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color);
+ submenu->draw(ci, Point2(display_width - submenu->get_width(), item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color);
}
+ // Text
item_ofs.y += font->get_ascent();
if (items[i].separator) {
if (text != String()) {
- int center = (get_size().width - font->get_string_size(text).width) / 2;
+ int center = (display_width - font->get_string_size(text).width) / 2;
font->draw(ci, Point2(center, item_ofs.y + Math::floor((h - font_h) / 2.0)), text, font_color_disabled);
}
} else {
@@ -527,19 +526,27 @@ void PopupMenu::_draw() {
font->draw(ci, item_ofs + Point2(0, Math::floor((h - font_h) / 2.0)), text, items[i].disabled ? font_color_disabled : (i == mouse_over ? font_color_hover : font_color));
}
+ // Accelerator / Shortcut
if (items[i].accel || (items[i].shortcut.is_valid() && items[i].shortcut->is_valid())) {
- //accelerator
- String text2 = _get_accel_text(i);
- item_ofs.x = size.width - style->get_margin(MARGIN_RIGHT) - font->get_string_size(text2).width;
- font->draw(ci, item_ofs + Point2(0, Math::floor((h - font_h) / 2.0)), text2, i == mouse_over ? font_color_hover : font_color_accel);
+ String sc_text = _get_accel_text(i);
+ item_ofs.x = display_width - font->get_string_size(sc_text).width;
+ font->draw(ci, item_ofs + Point2(0, Math::floor((h - font_h) / 2.0)), sc_text, i == mouse_over ? font_color_hover : font_color_accel);
}
+ // Cache the item vertical offset from the first item and the height
items.write[i]._ofs_cache = ofs.y;
+ items.write[i]._height_cache = h;
ofs.y += h;
}
}
+void PopupMenu::_draw_background() {
+ Ref<StyleBox> style = get_theme_stylebox("panel");
+ RID ci2 = margin_container->get_canvas_item();
+ style->draw(ci2, Rect2(Point2(), margin_container->get_size()));
+}
+
void PopupMenu::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
@@ -617,6 +624,13 @@ void PopupMenu::_notification(int p_what) {
if (get_window_id() != DisplayServer::INVALID_WINDOW_ID) {
set_process_internal(true);
}
+
+ // Set margin on the margin container
+ Ref<StyleBox> panel_style = get_theme_stylebox("panel");
+ margin_container->add_theme_constant_override("margin_top", panel_style->get_margin(Margin::MARGIN_TOP));
+ margin_container->add_theme_constant_override("margin_bottom", panel_style->get_margin(Margin::MARGIN_BOTTOM));
+ margin_container->add_theme_constant_override("margin_left", panel_style->get_margin(Margin::MARGIN_LEFT));
+ margin_container->add_theme_constant_override("margin_right", panel_style->get_margin(Margin::MARGIN_RIGHT));
}
} break;
}
@@ -698,7 +712,7 @@ void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int
}
#define ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global) \
- ERR_FAIL_COND_MSG(p_shortcut.is_null(), "Cannot add item with invalid ShortCut."); \
+ ERR_FAIL_COND_MSG(p_shortcut.is_null(), "Cannot add item with invalid Shortcut."); \
_ref_shortcut(p_shortcut); \
item.text = p_shortcut->get_name(); \
item.xl_text = tr(item.text); \
@@ -706,7 +720,7 @@ void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int
item.shortcut = p_shortcut; \
item.shortcut_is_global = p_global;
-void PopupMenu::add_shortcut(const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) {
+void PopupMenu::add_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) {
Item item;
ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
items.push_back(item);
@@ -714,7 +728,7 @@ void PopupMenu::add_shortcut(const Ref<ShortCut> &p_shortcut, int p_id, bool p_g
child_controls_changed();
}
-void PopupMenu::add_icon_shortcut(const Ref<Texture2D> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) {
+void PopupMenu::add_icon_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) {
Item item;
ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
item.icon = p_icon;
@@ -723,7 +737,7 @@ void PopupMenu::add_icon_shortcut(const Ref<Texture2D> &p_icon, const Ref<ShortC
child_controls_changed();
}
-void PopupMenu::add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) {
+void PopupMenu::add_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) {
Item item;
ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
@@ -732,7 +746,7 @@ void PopupMenu::add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_id, bo
child_controls_changed();
}
-void PopupMenu::add_icon_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) {
+void PopupMenu::add_icon_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) {
Item item;
ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
item.icon = p_icon;
@@ -742,7 +756,7 @@ void PopupMenu::add_icon_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<
child_controls_changed();
}
-void PopupMenu::add_radio_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) {
+void PopupMenu::add_radio_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) {
Item item;
ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
@@ -751,7 +765,7 @@ void PopupMenu::add_radio_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_
child_controls_changed();
}
-void PopupMenu::add_icon_radio_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id, bool p_global) {
+void PopupMenu::add_icon_radio_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id, bool p_global) {
Item item;
ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global);
item.icon = p_icon;
@@ -912,8 +926,8 @@ String PopupMenu::get_item_tooltip(int p_idx) const {
return items[p_idx].tooltip;
}
-Ref<ShortCut> PopupMenu::get_item_shortcut(int p_idx) const {
- ERR_FAIL_INDEX_V(p_idx, items.size(), Ref<ShortCut>());
+Ref<Shortcut> PopupMenu::get_item_shortcut(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx, items.size(), Ref<Shortcut>());
return items[p_idx].shortcut;
}
@@ -951,7 +965,7 @@ void PopupMenu::set_item_tooltip(int p_idx, const String &p_tooltip) {
control->update();
}
-void PopupMenu::set_item_shortcut(int p_idx, const Ref<ShortCut> &p_shortcut, bool p_global) {
+void PopupMenu::set_item_shortcut(int p_idx, const Ref<Shortcut> &p_shortcut, bool p_global) {
ERR_FAIL_INDEX(p_idx, items.size());
if (items[p_idx].shortcut.is_valid()) {
_unref_shortcut(items[p_idx].shortcut);
@@ -1190,7 +1204,7 @@ Array PopupMenu::_get_items() const {
return items;
}
-void PopupMenu::_ref_shortcut(Ref<ShortCut> p_sc) {
+void PopupMenu::_ref_shortcut(Ref<Shortcut> p_sc) {
if (!shortcut_refcount.has(p_sc)) {
shortcut_refcount[p_sc] = 1;
p_sc->connect("changed", callable_mp((CanvasItem *)this, &CanvasItem::update));
@@ -1199,7 +1213,7 @@ void PopupMenu::_ref_shortcut(Ref<ShortCut> p_sc) {
}
}
-void PopupMenu::_unref_shortcut(Ref<ShortCut> p_sc) {
+void PopupMenu::_unref_shortcut(Ref<Shortcut> p_sc) {
ERR_FAIL_COND(!shortcut_refcount.has(p_sc));
shortcut_refcount[p_sc]--;
if (shortcut_refcount[p_sc] == 0) {
@@ -1424,25 +1438,40 @@ void PopupMenu::_bind_methods() {
void PopupMenu::popup(const Rect2 &p_bounds) {
moved = Vector2();
- invalidated_click = true;
+ popup_time_msec = OS::get_singleton()->get_ticks_msec();
+ set_as_minsize();
Popup::popup(p_bounds);
}
PopupMenu::PopupMenu() {
+ // Margin Container
+ margin_container = memnew(MarginContainer);
+ margin_container->set_anchors_and_margins_preset(Control::PRESET_WIDE);
+ add_child(margin_container);
+ margin_container->connect("draw", callable_mp(this, &PopupMenu::_draw_background));
+
+ // Scroll Container
+ scroll_container = memnew(ScrollContainer);
+ scroll_container->set_clip_contents(true);
+ margin_container->add_child(scroll_container);
+
+ // The control which will display the items
control = memnew(Control);
- add_child(control);
-
+ control->set_clip_contents(false);
control->set_anchors_and_margins_preset(Control::PRESET_WIDE);
+ control->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ control->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ scroll_container->add_child(control);
+ control->connect("draw", callable_mp(this, &PopupMenu::_draw_items));
+
connect("window_input", callable_mp(this, &PopupMenu::_gui_input));
- control->connect("draw", callable_mp(this, &PopupMenu::_draw));
mouse_over = -1;
submenu_over = -1;
initial_button_mask = 0;
during_grabbed_click = false;
- invalidated_click = false;
- allow_search = false;
+ allow_search = true;
search_time_msec = 0;
search_string = "";
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index d03a14d6e4..e8f82ba869 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -31,7 +31,9 @@
#ifndef POPUP_MENU_H
#define POPUP_MENU_H
+#include "scene/gui/margin_container.h"
#include "scene/gui/popup.h"
+#include "scene/gui/scroll_container.h"
#include "scene/gui/shortcut.h"
class PopupMenu : public Popup {
@@ -57,11 +59,17 @@ class PopupMenu : public Popup {
String tooltip;
uint32_t accel;
int _ofs_cache;
+ int _height_cache;
int h_ofs;
- Ref<ShortCut> shortcut;
+ Ref<Shortcut> shortcut;
bool shortcut_is_global;
bool shortcut_is_disabled;
+ // Returns (0,0) if icon is null.
+ Size2 get_icon_size() const {
+ return icon.is_null() ? Size2() : icon->get_size();
+ }
+
Item() {
checked = false;
checkable_type = CHECKABLE_TYPE_NONE;
@@ -71,6 +79,7 @@ class PopupMenu : public Popup {
accel = 0;
disabled = false;
_ofs_cache = 0;
+ _height_cache = 0;
h_ofs = 0;
shortcut_is_global = false;
shortcut_is_disabled = false;
@@ -87,13 +96,16 @@ class PopupMenu : public Popup {
Rect2 parent_rect;
String _get_accel_text(int p_item) const;
int _get_mouse_over(const Point2 &p_over) const;
- virtual Size2 _get_contents_minimum_size() const;
- void _scroll(float p_factor, const Point2 &p_over);
+ virtual Size2 _get_contents_minimum_size() const override;
+
+ int _get_items_total_height() const;
+ void _scroll_to_item(int p_item);
+
void _gui_input(const Ref<InputEvent> &p_event);
void _activate_submenu(int over);
void _submenu_timeout();
- bool invalidated_click;
+ uint64_t popup_time_msec = 0;
bool hide_on_item_selection;
bool hide_on_checkable_item_selection;
bool hide_on_multistate_item_selection;
@@ -102,18 +114,21 @@ class PopupMenu : public Popup {
Array _get_items() const;
void _set_items(const Array &p_items);
- Map<Ref<ShortCut>, int> shortcut_refcount;
+ Map<Ref<Shortcut>, int> shortcut_refcount;
- void _ref_shortcut(Ref<ShortCut> p_sc);
- void _unref_shortcut(Ref<ShortCut> p_sc);
+ void _ref_shortcut(Ref<Shortcut> p_sc);
+ void _unref_shortcut(Ref<Shortcut> p_sc);
bool allow_search;
uint64_t search_time_msec;
String search_string;
+ MarginContainer *margin_container;
+ ScrollContainer *scroll_container;
Control *control;
- void _draw();
+ void _draw_items();
+ void _draw_background();
protected:
friend class MenuButton;
@@ -130,12 +145,12 @@ public:
void add_multistate_item(const String &p_label, int p_max_states, int p_default_state = 0, int p_id = -1, uint32_t p_accel = 0);
- void add_shortcut(const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false);
- void add_icon_shortcut(const Ref<Texture2D> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false);
- void add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false);
- void add_icon_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false);
- void add_radio_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false);
- void add_icon_radio_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<ShortCut> &p_shortcut, int p_id = -1, bool p_global = false);
+ void add_shortcut(const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false);
+ void add_icon_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false);
+ void add_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false);
+ void add_icon_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false);
+ void add_radio_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false);
+ void add_icon_radio_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortcut> &p_shortcut, int p_id = -1, bool p_global = false);
void add_submenu_item(const String &p_label, const String &p_submenu, int p_id = -1);
@@ -151,7 +166,7 @@ public:
void set_item_as_checkable(int p_idx, bool p_checkable);
void set_item_as_radio_checkable(int p_idx, bool p_radio_checkable);
void set_item_tooltip(int p_idx, const String &p_tooltip);
- void set_item_shortcut(int p_idx, const Ref<ShortCut> &p_shortcut, bool p_global = false);
+ void set_item_shortcut(int p_idx, const Ref<Shortcut> &p_shortcut, bool p_global = false);
void set_item_h_offset(int p_idx, int p_offset);
void set_item_multistate(int p_idx, int p_state);
void toggle_item_multistate(int p_idx);
@@ -174,7 +189,7 @@ public:
bool is_item_radio_checkable(int p_idx) const;
bool is_item_shortcut_disabled(int p_idx) const;
String get_item_tooltip(int p_idx) const;
- Ref<ShortCut> get_item_shortcut(int p_idx) const;
+ Ref<Shortcut> get_item_shortcut(int p_idx) const;
int get_item_state(int p_idx) const;
int get_current_index() const;
@@ -193,7 +208,7 @@ public:
virtual String get_tooltip(const Point2 &p_pos) const;
- virtual void get_translatable_strings(List<String> *p_strings) const;
+ virtual void get_translatable_strings(List<String> *p_strings) const override;
void add_autohide_area(const Rect2 &p_area);
void clear_autohide_areas();
diff --git a/scene/gui/progress_bar.h b/scene/gui/progress_bar.h
index d8eba921a3..f00f993adf 100644
--- a/scene/gui/progress_bar.h
+++ b/scene/gui/progress_bar.h
@@ -46,7 +46,7 @@ public:
void set_percent_visible(bool p_visible);
bool is_percent_visible() const;
- Size2 get_minimum_size() const;
+ Size2 get_minimum_size() const override;
ProgressBar();
};
diff --git a/scene/gui/range.h b/scene/gui/range.h
index fe43985d0d..9ba367aaa4 100644
--- a/scene/gui/range.h
+++ b/scene/gui/range.h
@@ -94,7 +94,7 @@ public:
void share(Range *p_range);
void unshare();
- virtual String get_configuration_warning() const;
+ virtual String get_configuration_warning() const override;
Range();
~Range();
diff --git a/scene/gui/rich_text_effect.h b/scene/gui/rich_text_effect.h
index 77c82aa780..a5401f7eaa 100644
--- a/scene/gui/rich_text_effect.h
+++ b/scene/gui/rich_text_effect.h
@@ -59,7 +59,7 @@ public:
bool visibility;
Point2 offset;
Color color;
- CharType character;
+ char32_t character;
float elapsed_time;
Dictionary environment;
@@ -79,7 +79,7 @@ public:
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; }
+ void set_character(int p_char) { character = (char32_t)p_char; }
Dictionary get_environment() { return environment; }
void set_environment(Dictionary p_environment) { environment = p_environment; }
};
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index a57408b83b..e8acac172c 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -188,7 +188,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
wofs += line_ofs;
}
- int begin = wofs;
+ int begin = margin;
Ref<Font> cfont = _find_font(it);
if (cfont.is_null()) {
@@ -256,6 +256,11 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
lh = line < l.height_caches.size() ? l.height_caches[line] : 1; \
line_ascent = line < l.ascent_caches.size() ? l.ascent_caches[line] : 1; \
line_descent = line < l.descent_caches.size() ? l.descent_caches[line] : 1; \
+ if (align != ALIGN_FILL) { \
+ if (line < l.offset_caches.size()) { \
+ wofs = l.offset_caches[line]; \
+ } \
+ } \
} \
if (p_mode == PROCESS_POINTER && r_click_item && p_click_pos.y >= p_ofs.y + y && p_click_pos.y <= p_ofs.y + y + lh && p_click_pos.x < p_ofs.x + wofs) { \
if (r_outside) \
@@ -349,8 +354,8 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
font = p_base_font;
}
- const CharType *c = text->text.c_str();
- const CharType *cf = c;
+ const char32_t *c = text->text.get_data();
+ const char32_t *cf = c;
int ascent = font->get_ascent();
int descent = font->get_descent();
@@ -456,7 +461,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
bool selected = false;
Color fx_color = Color(color);
Point2 fx_offset;
- CharType fx_char = c[i];
+ char32_t fx_char = c[i];
if (selection.active) {
int cofs = (&c[i]) - cf;
@@ -646,7 +651,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
}
if (p_mode == PROCESS_DRAW && visible) {
- img->image->draw_rect(ci, Rect2(p_ofs + Point2(align_ofs + wofs, y + lh - font->get_descent() - img->size.height), img->size));
+ img->image->draw_rect(ci, Rect2(p_ofs + Point2(align_ofs + wofs, y + lh - font->get_descent() - img->size.height), img->size), false, img->color);
}
p_char_count++;
@@ -873,7 +878,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
}
void RichTextLabel::_scroll_changed(double) {
- if (updating_scroll || !scroll_active) {
+ if (updating_scroll) {
return;
}
@@ -1015,7 +1020,8 @@ void RichTextLabel::_notification(int p_what) {
visible_line_count = 0;
while (y < size.height && from_line < main->lines.size()) {
- visible_line_count += _process_line(main, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, PROCESS_DRAW, base_font, base_color, font_color_shadow, use_outline, shadow_ofs, Point2i(), nullptr, nullptr, nullptr, total_chars);
+ visible_line_count++;
+ _process_line(main, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, PROCESS_DRAW, base_font, base_color, font_color_shadow, use_outline, shadow_ofs, Point2i(), nullptr, nullptr, nullptr, total_chars);
total_chars += main->lines[from_line].char_count;
from_line++;
@@ -1443,6 +1449,46 @@ void RichTextLabel::_fetch_item_fx_stack(Item *p_item, Vector<ItemFX *> &r_stack
}
}
+Color RichTextLabel::_get_color_from_string(const String &p_color_str, const Color &p_default_color) {
+ if (p_color_str.begins_with("#")) {
+ return Color::html(p_color_str);
+ } else if (p_color_str == "aqua") {
+ return Color(0, 1, 1);
+ } else if (p_color_str == "black") {
+ return Color(0, 0, 0);
+ } else if (p_color_str == "blue") {
+ return Color(0, 0, 1);
+ } else if (p_color_str == "fuchsia") {
+ return Color(1, 0, 1);
+ } else if (p_color_str == "gray" || p_color_str == "grey") {
+ return Color(0.5, 0.5, 0.5);
+ } else if (p_color_str == "green") {
+ return Color(0, 0.5, 0);
+ } else if (p_color_str == "lime") {
+ return Color(0, 1, 0);
+ } else if (p_color_str == "maroon") {
+ return Color(0.5, 0, 0);
+ } else if (p_color_str == "navy") {
+ return Color(0, 0, 0.5);
+ } else if (p_color_str == "olive") {
+ return Color(0.5, 0.5, 0);
+ } else if (p_color_str == "purple") {
+ return Color(0.5, 0, 0.5);
+ } else if (p_color_str == "red") {
+ return Color(1, 0, 0);
+ } else if (p_color_str == "silver") {
+ return Color(0.75, 0.75, 0.75);
+ } else if (p_color_str == "teal") {
+ return Color(0, 0.5, 0.5);
+ } else if (p_color_str == "white") {
+ return Color(1, 1, 1);
+ } else if (p_color_str == "yellow") {
+ return Color(1, 1, 0);
+ } else {
+ return p_default_color;
+ }
+}
+
bool RichTextLabel::_find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item) {
Item *item = p_item;
@@ -1525,6 +1571,10 @@ void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) {
}
updating_scroll = false;
+
+ if (fit_content_height) {
+ minimum_size_changed();
+ }
}
void RichTextLabel::_invalidate_current_line(ItemFrame *p_frame) {
@@ -1636,7 +1686,7 @@ void RichTextLabel::_remove_item(Item *p_item, const int p_line, const int p_sub
}
}
-void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width, const int p_height) {
+void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width, const int p_height, const Color &p_color) {
if (current->type == ITEM_TABLE) {
return;
}
@@ -1645,6 +1695,7 @@ void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width,
ItemImage *item = memnew(ItemImage);
item->image = p_image;
+ item->color = p_color;
if (p_width > 0) {
// custom width
@@ -1924,6 +1975,9 @@ void RichTextLabel::clear() {
selection.click = nullptr;
selection.active = false;
current_idx = 1;
+ if (scroll_follow) {
+ scroll_following = true;
+ }
if (fixed_width != -1) {
minimum_size_changed();
@@ -1940,6 +1994,17 @@ int RichTextLabel::get_tab_size() const {
return tab_size;
}
+void RichTextLabel::set_fit_content_height(bool p_enabled) {
+ if (p_enabled != fit_content_height) {
+ fit_content_height = p_enabled;
+ minimum_size_changed();
+ }
+}
+
+bool RichTextLabel::is_fit_content_height_enabled() const {
+ return fit_content_height;
+}
+
void RichTextLabel::set_meta_underline(bool p_underline) {
underline_meta = p_underline;
update();
@@ -1967,6 +2032,7 @@ void RichTextLabel::set_scroll_active(bool p_active) {
}
scroll_active = p_active;
+ vscroll->set_drag_node_enabled(p_active);
update();
}
@@ -2034,7 +2100,32 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
String tag = p_bbcode.substr(brk_pos + 1, brk_end - brk_pos - 1);
Vector<String> split_tag_block = tag.split(" ", false);
- String bbcode = !split_tag_block.empty() ? split_tag_block[0] : "";
+
+ // Find optional parameters.
+ String bbcode_name;
+ typedef Map<String, String> OptionMap;
+ OptionMap bbcode_options;
+ if (!split_tag_block.empty()) {
+ bbcode_name = split_tag_block[0];
+ for (int i = 1; i < split_tag_block.size(); i++) {
+ const String &expr = split_tag_block[i];
+ int value_pos = expr.find("=");
+ if (value_pos > -1) {
+ bbcode_options[expr.substr(0, value_pos)] = expr.substr(value_pos + 1);
+ }
+ }
+ } else {
+ bbcode_name = tag;
+ }
+
+ // Find main parameter.
+ String bbcode_value;
+ int main_value_pos = bbcode_name.find("=");
+ if (main_value_pos > -1) {
+ bbcode_value = bbcode_name.substr(main_value_pos + 1);
+ bbcode_name = bbcode_name.substr(0, main_value_pos);
+ }
+
if (tag.begins_with("/") && tag_stack.size()) {
bool tag_ok = tag_stack.size() && tag_stack.front()->get() == tag.substr(1, tag.length());
@@ -2160,7 +2251,7 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
push_meta(url);
pos = brk_end + 1;
tag_stack.push_front("url");
- } else if (tag == "img") {
+ } else if (bbcode_name == "img") {
int end = p_bbcode.find("[", brk_end);
if (end == -1) {
end = p_bbcode.length();
@@ -2170,80 +2261,42 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
Ref<Texture2D> texture = ResourceLoader::load(image, "Texture2D");
if (texture.is_valid()) {
- add_image(texture);
- }
-
- pos = end;
- tag_stack.push_front(tag);
- } else if (tag.begins_with("img=")) {
- int width = 0;
- int height = 0;
-
- String params = tag.substr(4, tag.length());
- int sep = params.find("x");
- if (sep == -1) {
- width = params.to_int();
- } else {
- width = params.substr(0, sep).to_int();
- height = params.substr(sep + 1, params.length()).to_int();
- }
+ Color color = Color(1.0, 1.0, 1.0);
+ OptionMap::Element *color_option = bbcode_options.find("color");
+ if (color_option) {
+ color = _get_color_from_string(color_option->value(), color);
+ }
- int end = p_bbcode.find("[", brk_end);
- if (end == -1) {
- end = p_bbcode.length();
- }
+ int width = 0;
+ int height = 0;
+ if (!bbcode_value.empty()) {
+ int sep = bbcode_value.find("x");
+ if (sep == -1) {
+ width = bbcode_value.to_int();
+ } else {
+ width = bbcode_value.substr(0, sep).to_int();
+ height = bbcode_value.substr(sep + 1).to_int();
+ }
+ } else {
+ OptionMap::Element *width_option = bbcode_options.find("width");
+ if (width_option) {
+ width = width_option->value().to_int();
+ }
- String image = p_bbcode.substr(brk_end + 1, end - brk_end - 1);
+ OptionMap::Element *height_option = bbcode_options.find("height");
+ if (height_option) {
+ height = height_option->value().to_int();
+ }
+ }
- Ref<Texture2D> texture = ResourceLoader::load(image, "Texture");
- if (texture.is_valid()) {
- add_image(texture, width, height);
+ add_image(texture, width, height, color);
}
pos = end;
- tag_stack.push_front("img");
+ tag_stack.push_front(bbcode_name);
} else if (tag.begins_with("color=")) {
- String col = tag.substr(6, tag.length());
- Color color;
-
- if (col.begins_with("#")) {
- color = Color::html(col);
- } else if (col == "aqua") {
- color = Color(0, 1, 1);
- } else if (col == "black") {
- color = Color(0, 0, 0);
- } else if (col == "blue") {
- color = Color(0, 0, 1);
- } else if (col == "fuchsia") {
- color = Color(1, 0, 1);
- } else if (col == "gray" || col == "grey") {
- color = Color(0.5, 0.5, 0.5);
- } else if (col == "green") {
- color = Color(0, 0.5, 0);
- } else if (col == "lime") {
- color = Color(0, 1, 0);
- } else if (col == "maroon") {
- color = Color(0.5, 0, 0);
- } else if (col == "navy") {
- color = Color(0, 0, 0.5);
- } else if (col == "olive") {
- color = Color(0.5, 0.5, 0);
- } else if (col == "purple") {
- color = Color(0.5, 0, 0.5);
- } else if (col == "red") {
- color = Color(1, 0, 0);
- } else if (col == "silver") {
- color = Color(0.75, 0.75, 0.75);
- } else if (col == "teal") {
- color = Color(0, 0.5, 0.5);
- } else if (col == "white") {
- color = Color(1, 1, 1);
- } else if (col == "yellow") {
- color = Color(1, 1, 0);
- } else {
- color = base_color;
- }
-
+ String color_str = tag.substr(6, tag.length());
+ Color color = _get_color_from_string(color_str, base_color);
push_color(color);
pos = brk_end + 1;
tag_stack.push_front("color");
@@ -2261,113 +2314,90 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
pos = brk_end + 1;
tag_stack.push_front("font");
- } else if (bbcode == "fade") {
- int startIndex = 0;
- int length = 10;
+ } else if (bbcode_name == "fade") {
+ int start_index = 0;
+ OptionMap::Element *start_option = bbcode_options.find("start");
+ if (start_option) {
+ start_index = start_option->value().to_int();
+ }
- if (split_tag_block.size() > 1) {
- split_tag_block.remove(0);
- for (int i = 0; i < split_tag_block.size(); i++) {
- String expr = split_tag_block[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();
- }
- }
+ int length = 10;
+ OptionMap::Element *length_option = bbcode_options.find("length");
+ if (length_option) {
+ length = length_option->value().to_int();
}
- push_fade(startIndex, length);
+ push_fade(start_index, length);
pos = brk_end + 1;
tag_stack.push_front("fade");
- } else if (bbcode == "shake") {
+ } else if (bbcode_name == "shake") {
int strength = 5;
- float rate = 20.0f;
+ OptionMap::Element *strength_option = bbcode_options.find("level");
+ if (strength_option) {
+ strength = strength_option->value().to_int();
+ }
- if (split_tag_block.size() > 1) {
- split_tag_block.remove(0);
- for (int i = 0; i < split_tag_block.size(); i++) {
- String expr = split_tag_block[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();
- }
- }
+ float rate = 20.0f;
+ OptionMap::Element *rate_option = bbcode_options.find("rate");
+ if (rate_option) {
+ rate = rate_option->value().to_float();
}
push_shake(strength, rate);
pos = brk_end + 1;
tag_stack.push_front("shake");
set_process_internal(true);
- } else if (bbcode == "wave") {
+ } else if (bbcode_name == "wave") {
float amplitude = 20.0f;
- float period = 5.0f;
+ OptionMap::Element *amplitude_option = bbcode_options.find("amp");
+ if (amplitude_option) {
+ amplitude = amplitude_option->value().to_float();
+ }
- if (split_tag_block.size() > 1) {
- split_tag_block.remove(0);
- for (int i = 0; i < split_tag_block.size(); i++) {
- String expr = split_tag_block[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();
- }
- }
+ float period = 5.0f;
+ OptionMap::Element *period_option = bbcode_options.find("freq");
+ if (period_option) {
+ period = period_option->value().to_float();
}
push_wave(period, amplitude);
pos = brk_end + 1;
tag_stack.push_front("wave");
set_process_internal(true);
- } else if (bbcode == "tornado") {
+ } else if (bbcode_name == "tornado") {
float radius = 10.0f;
- float frequency = 1.0f;
+ OptionMap::Element *radius_option = bbcode_options.find("radius");
+ if (radius_option) {
+ radius = radius_option->value().to_float();
+ }
- if (split_tag_block.size() > 1) {
- split_tag_block.remove(0);
- for (int i = 0; i < split_tag_block.size(); i++) {
- String expr = split_tag_block[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();
- }
- }
+ float frequency = 1.0f;
+ OptionMap::Element *frequency_option = bbcode_options.find("freq");
+ if (frequency_option) {
+ frequency = frequency_option->value().to_float();
}
push_tornado(frequency, radius);
pos = brk_end + 1;
tag_stack.push_front("tornado");
set_process_internal(true);
- } else if (bbcode == "rainbow") {
+ } else if (bbcode_name == "rainbow") {
float saturation = 0.8f;
+ OptionMap::Element *saturation_option = bbcode_options.find("sat");
+ if (saturation_option) {
+ saturation = saturation_option->value().to_float();
+ }
+
float value = 0.8f;
- float frequency = 1.0f;
+ OptionMap::Element *value_option = bbcode_options.find("val");
+ if (value_option) {
+ value = value_option->value().to_float();
+ }
- if (split_tag_block.size() > 1) {
- split_tag_block.remove(0);
- for (int i = 0; i < split_tag_block.size(); i++) {
- String expr = split_tag_block[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();
- }
- }
+ float frequency = 1.0f;
+ OptionMap::Element *frequency_option = bbcode_options.find("freq");
+ if (frequency_option) {
+ frequency = frequency_option->value().to_float();
}
push_rainbow(saturation, value, frequency);
@@ -2398,6 +2428,17 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
}
}
+ Vector<ItemFX *> fx_items;
+ for (List<Item *>::Element *E = main->subitems.front(); E; E = E->next()) {
+ Item *subitem = static_cast<Item *>(E->get());
+ _fetch_item_fx_stack(subitem, fx_items);
+
+ if (fx_items.size()) {
+ set_process_internal(true);
+ break;
+ }
+ }
+
return OK;
}
@@ -2488,9 +2529,9 @@ bool RichTextLabel::search(const String &p_string, bool p_from_selection, bool p
return false;
}
-void RichTextLabel::selection_copy() {
+String RichTextLabel::get_selected_text() {
if (!selection.active || !selection.enabled) {
- return;
+ return "";
}
String text;
@@ -2520,6 +2561,12 @@ void RichTextLabel::selection_copy() {
item = _get_next_item(item, true);
}
+ return text;
+}
+
+void RichTextLabel::selection_copy() {
+ String text = get_selected_text();
+
if (text != "") {
DisplayServer::get_singleton()->clipboard_set(text);
}
@@ -2621,7 +2668,7 @@ void RichTextLabel::install_effect(const Variant effect) {
}
}
-int RichTextLabel::get_content_height() {
+int RichTextLabel::get_content_height() const {
int total_height = 0;
if (main->lines.size()) {
total_height = main->lines[main->lines.size() - 1].height_accum_cache + get_theme_stylebox("normal")->get_minimum_size().height;
@@ -2634,7 +2681,7 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_text"), &RichTextLabel::get_text);
ClassDB::bind_method(D_METHOD("add_text", "text"), &RichTextLabel::add_text);
ClassDB::bind_method(D_METHOD("set_text", "text"), &RichTextLabel::set_text);
- ClassDB::bind_method(D_METHOD("add_image", "image", "width", "height"), &RichTextLabel::add_image, DEFVAL(0), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("add_image", "image", "width", "height", "color"), &RichTextLabel::add_image, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0)));
ClassDB::bind_method(D_METHOD("newline"), &RichTextLabel::add_newline);
ClassDB::bind_method(D_METHOD("remove_line", "line"), &RichTextLabel::remove_line);
ClassDB::bind_method(D_METHOD("push_font", "font"), &RichTextLabel::push_font);
@@ -2676,6 +2723,9 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_tab_size", "spaces"), &RichTextLabel::set_tab_size);
ClassDB::bind_method(D_METHOD("get_tab_size"), &RichTextLabel::get_tab_size);
+ ClassDB::bind_method(D_METHOD("set_fit_content_height", "enabled"), &RichTextLabel::set_fit_content_height);
+ ClassDB::bind_method(D_METHOD("is_fit_content_height_enabled"), &RichTextLabel::is_fit_content_height_enabled);
+
ClassDB::bind_method(D_METHOD("set_selection_enabled", "enabled"), &RichTextLabel::set_selection_enabled);
ClassDB::bind_method(D_METHOD("is_selection_enabled"), &RichTextLabel::is_selection_enabled);
@@ -2718,13 +2768,15 @@ void RichTextLabel::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_size", PROPERTY_HINT_RANGE, "0,24,1"), "set_tab_size", "get_tab_size");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fit_content_height"), "set_fit_content_height", "is_fit_content_height_enabled");
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_active"), "set_scroll_active", "is_scroll_active");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_following"), "set_scroll_follow", "is_scroll_following");
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_PROPERTY(PropertyInfo(Variant::ARRAY, "custom_effects", PROPERTY_HINT_ARRAY_TYPE, "RichTextEffect", (PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE)), "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)));
@@ -2784,12 +2836,17 @@ void RichTextLabel::set_fixed_size_to_width(int p_width) {
}
Size2 RichTextLabel::get_minimum_size() const {
+ Size2 size(0, 0);
if (fixed_width != -1) {
+ size.x = fixed_width;
+ }
+
+ if (fixed_width != -1 || fit_content_height) {
const_cast<RichTextLabel *>(this)->_validate_line_caches(main);
- return Size2(fixed_width, const_cast<RichTextLabel *>(this)->get_content_height());
+ size.y = get_content_height();
}
- return Size2();
+ return size;
}
Ref<RichTextEffect> RichTextLabel::_get_custom_effect_by_code(String p_bbcode_identifier) {
@@ -2847,7 +2904,7 @@ Dictionary RichTextLabel::parse_expressions_for_values(Vector<String> p_expressi
a.append(false);
}
} else if (!decimal.search(values[j]).is_null()) {
- a.append(values[j].to_double());
+ a.append(values[j].to_float());
} else if (!numerical.search(values[j]).is_null()) {
a.append(values[j].to_int());
} else {
@@ -2909,6 +2966,8 @@ RichTextLabel::RichTextLabel() {
visible_line_count = 0;
fixed_width = -1;
+ fit_content_height = false;
+
set_clip_contents(true);
}
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index 019edf5d45..c5ed1cb3ef 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -149,6 +149,7 @@ private:
struct ItemImage : public Item {
Ref<Texture2D> image;
Size2 size;
+ Color color;
ItemImage() { type = ITEM_IMAGE; }
};
@@ -381,6 +382,8 @@ private:
bool _find_by_type(Item *p_item, ItemType p_type);
void _fetch_item_fx_stack(Item *p_item, Vector<ItemFX *> &r_stack);
+ static Color _get_color_from_string(const String &p_color_str, const Color &p_default_color);
+
void _update_scroll();
void _update_fx(ItemFrame *p_frame, float p_delta_time);
void _scroll_changed(double);
@@ -400,13 +403,15 @@ private:
int fixed_width;
+ bool fit_content_height;
+
protected:
void _notification(int p_what);
public:
String get_text();
void add_text(const String &p_text);
- void add_image(const Ref<Texture2D> &p_image, const int p_width = 0, const int p_height = 0);
+ void add_image(const Ref<Texture2D> &p_image, const int p_width = 0, const int p_height = 0, const Color &p_color = Color(1.0, 1.0, 1.0));
void add_newline();
bool remove_line(const int p_line);
void push_font(const Ref<Font> &p_font);
@@ -453,20 +458,24 @@ public:
void set_tab_size(int p_spaces);
int get_tab_size() const;
+ void set_fit_content_height(bool p_enabled);
+ bool is_fit_content_height_enabled() const;
+
bool search(const String &p_string, bool p_from_selection = false, bool p_search_previous = false);
void scroll_to_line(int p_line);
int get_line_count() const;
int get_visible_line_count() const;
- int get_content_height();
+ int get_content_height() const;
VScrollBar *get_v_scroll() { return vscroll; }
- virtual CursorShape get_cursor_shape(const Point2 &p_pos) const;
+ virtual CursorShape get_cursor_shape(const Point2 &p_pos) const override;
void set_selection_enabled(bool p_enabled);
bool is_selection_enabled() const;
+ String get_selected_text();
void selection_copy();
Error parse_bbcode(const String &p_bbcode);
@@ -493,7 +502,7 @@ public:
void install_effect(const Variant effect);
void set_fixed_size_to_width(int p_width);
- virtual Size2 get_minimum_size() const;
+ virtual Size2 get_minimum_size() const override;
RichTextLabel();
~RichTextLabel();
diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp
index e7950bec98..0e9ef71892 100644
--- a/scene/gui/scroll_bar.cpp
+++ b/scene/gui/scroll_bar.cpp
@@ -506,6 +506,10 @@ void ScrollBar::_drag_node_exit() {
}
void ScrollBar::_drag_node_input(const Ref<InputEvent> &p_input) {
+ if (!drag_node_enabled) {
+ return;
+ }
+
Ref<InputEventMouseButton> mb = p_input;
if (mb.is_valid()) {
@@ -518,7 +522,7 @@ void ScrollBar::_drag_node_input(const Ref<InputEvent> &p_input) {
drag_node_accum = Vector2();
last_drag_node_accum = Vector2();
drag_node_from = Vector2(orientation == HORIZONTAL ? get_value() : 0, orientation == VERTICAL ? get_value() : 0);
- drag_node_touching = !DisplayServer::get_singleton()->screen_is_touchscreen(DisplayServer::get_singleton()->window_get_current_screen(get_viewport()->get_window_id()));
+ drag_node_touching = DisplayServer::get_singleton()->screen_is_touchscreen(DisplayServer::get_singleton()->window_get_current_screen(get_viewport()->get_window_id()));
drag_node_touching_deaccel = false;
time_since_motion = 0;
@@ -590,6 +594,10 @@ NodePath ScrollBar::get_drag_node() const {
return drag_node_path;
}
+void ScrollBar::set_drag_node_enabled(bool p_enable) {
+ drag_node_enabled = p_enable;
+}
+
void ScrollBar::set_smooth_scroll_enabled(bool p_enable) {
smooth_scroll_enabled = p_enable;
}
@@ -610,19 +618,6 @@ void ScrollBar::_bind_methods() {
ScrollBar::ScrollBar(Orientation p_orientation) {
orientation = p_orientation;
- highlight = HIGHLIGHT_NONE;
- custom_step = -1;
- drag_node = nullptr;
-
- drag.active = false;
-
- drag_node_speed = Vector2();
- drag_node_touching = false;
- drag_node_touching_deaccel = false;
-
- scrolling = false;
- target_scroll = 0;
- smooth_scroll_enabled = false;
if (focus_by_default) {
set_focus_mode(FOCUS_ALL);
diff --git a/scene/gui/scroll_bar.h b/scene/gui/scroll_bar.h
index d2641b14f3..6ae76e453a 100644
--- a/scene/gui/scroll_bar.h
+++ b/scene/gui/scroll_bar.h
@@ -47,12 +47,12 @@ class ScrollBar : public Range {
Orientation orientation;
Size2 size;
- float custom_step;
+ float custom_step = -1;
- HighlightStatus highlight;
+ HighlightStatus highlight = HIGHLIGHT_NONE;
struct Drag {
- bool active;
+ bool active = false;
float pos_at_click;
float value_at_click;
} drag;
@@ -66,22 +66,23 @@ class ScrollBar : public Range {
static void set_can_focus_by_default(bool p_can_focus);
- Node *drag_node;
+ Node *drag_node = nullptr;
NodePath drag_node_path;
+ bool drag_node_enabled = true;
- Vector2 drag_node_speed;
+ Vector2 drag_node_speed = Vector2();
Vector2 drag_node_accum;
Vector2 drag_node_from;
Vector2 last_drag_node_accum;
float last_drag_node_time;
float time_since_motion;
- bool drag_node_touching;
- bool drag_node_touching_deaccel;
+ bool drag_node_touching = false;
+ bool drag_node_touching_deaccel = false;
bool click_handled;
- bool scrolling;
- double target_scroll;
- bool smooth_scroll_enabled;
+ bool scrolling = false;
+ double target_scroll = 0;
+ bool smooth_scroll_enabled = false;
void _drag_node_exit();
void _drag_node_input(const Ref<InputEvent> &p_input);
@@ -99,11 +100,12 @@ public:
void set_drag_node(const NodePath &p_path);
NodePath get_drag_node() const;
+ void set_drag_node_enabled(bool p_enable);
void set_smooth_scroll_enabled(bool p_enable);
bool is_smooth_scroll_enabled() const;
- virtual Size2 get_minimum_size() const;
+ virtual Size2 get_minimum_size() const override;
ScrollBar(Orientation p_orientation = VERTICAL);
~ScrollBar();
};
diff --git a/scene/gui/scroll_container.h b/scene/gui/scroll_container.h
index 321e0e2c5a..b28d66ed53 100644
--- a/scene/gui/scroll_container.h
+++ b/scene/gui/scroll_container.h
@@ -66,7 +66,7 @@ class ScrollContainer : public Container {
void _cancel_drag();
protected:
- Size2 get_minimum_size() const;
+ Size2 get_minimum_size() const override;
void _gui_input(const Ref<InputEvent> &p_gui_input);
void _notification(int p_what);
@@ -99,9 +99,9 @@ public:
HScrollBar *get_h_scrollbar();
VScrollBar *get_v_scrollbar();
- virtual bool clips_input() const;
+ virtual bool clips_input() const override;
- virtual String get_configuration_warning() const;
+ virtual String get_configuration_warning() const override;
ScrollContainer();
};
diff --git a/scene/gui/separator.h b/scene/gui/separator.h
index f7e5ef2c6b..eec989cfea 100644
--- a/scene/gui/separator.h
+++ b/scene/gui/separator.h
@@ -40,7 +40,7 @@ protected:
void _notification(int p_what);
public:
- virtual Size2 get_minimum_size() const;
+ virtual Size2 get_minimum_size() const override;
Separator();
~Separator();
diff --git a/scene/gui/shortcut.cpp b/scene/gui/shortcut.cpp
index 9f5b9c40c2..f8c7bc44a7 100644
--- a/scene/gui/shortcut.cpp
+++ b/scene/gui/shortcut.cpp
@@ -32,20 +32,20 @@
#include "core/os/keyboard.h"
-void ShortCut::set_shortcut(const Ref<InputEvent> &p_shortcut) {
+void Shortcut::set_shortcut(const Ref<InputEvent> &p_shortcut) {
shortcut = p_shortcut;
emit_changed();
}
-Ref<InputEvent> ShortCut::get_shortcut() const {
+Ref<InputEvent> Shortcut::get_shortcut() const {
return shortcut;
}
-bool ShortCut::is_shortcut(const Ref<InputEvent> &p_event) const {
+bool Shortcut::is_shortcut(const Ref<InputEvent> &p_event) const {
return shortcut.is_valid() && shortcut->shortcut_match(p_event);
}
-String ShortCut::get_as_text() const {
+String Shortcut::get_as_text() const {
if (shortcut.is_valid()) {
return shortcut->as_text();
} else {
@@ -53,21 +53,21 @@ String ShortCut::get_as_text() const {
}
}
-bool ShortCut::is_valid() const {
+bool Shortcut::is_valid() const {
return shortcut.is_valid();
}
-void ShortCut::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_shortcut", "event"), &ShortCut::set_shortcut);
- ClassDB::bind_method(D_METHOD("get_shortcut"), &ShortCut::get_shortcut);
+void Shortcut::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_shortcut", "event"), &Shortcut::set_shortcut);
+ ClassDB::bind_method(D_METHOD("get_shortcut"), &Shortcut::get_shortcut);
- ClassDB::bind_method(D_METHOD("is_valid"), &ShortCut::is_valid);
+ ClassDB::bind_method(D_METHOD("is_valid"), &Shortcut::is_valid);
- ClassDB::bind_method(D_METHOD("is_shortcut", "event"), &ShortCut::is_shortcut);
- ClassDB::bind_method(D_METHOD("get_as_text"), &ShortCut::get_as_text);
+ ClassDB::bind_method(D_METHOD("is_shortcut", "event"), &Shortcut::is_shortcut);
+ ClassDB::bind_method(D_METHOD("get_as_text"), &Shortcut::get_as_text);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shortcut", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), "set_shortcut", "get_shortcut");
}
-ShortCut::ShortCut() {
+Shortcut::Shortcut() {
}
diff --git a/scene/gui/shortcut.h b/scene/gui/shortcut.h
index 063d4e43dc..0d7809e5cf 100644
--- a/scene/gui/shortcut.h
+++ b/scene/gui/shortcut.h
@@ -34,8 +34,8 @@
#include "core/input/input_event.h"
#include "core/resource.h"
-class ShortCut : public Resource {
- GDCLASS(ShortCut, Resource);
+class Shortcut : public Resource {
+ GDCLASS(Shortcut, Resource);
Ref<InputEvent> shortcut;
@@ -50,7 +50,7 @@ public:
String get_as_text() const;
- ShortCut();
+ Shortcut();
};
#endif // SHORTCUT_H
diff --git a/scene/gui/slider.h b/scene/gui/slider.h
index 6f8f7cc7d8..b4b56f2856 100644
--- a/scene/gui/slider.h
+++ b/scene/gui/slider.h
@@ -56,7 +56,7 @@ protected:
bool ticks_on_borders;
public:
- virtual Size2 get_minimum_size() const;
+ virtual Size2 get_minimum_size() const override;
void set_custom_step(float p_custom_step);
float get_custom_step() const;
diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp
index 3670f13705..ae2f99e91d 100644
--- a/scene/gui/spin_box.cpp
+++ b/scene/gui/spin_box.cpp
@@ -62,8 +62,8 @@ void SpinBox::_text_entered(const String &p_string) {
Variant value = expr->execute(Array(), nullptr, false);
if (value.get_type() != Variant::NIL) {
set_value(value);
- _value_changed(0);
}
+ _value_changed(0);
}
LineEdit *SpinBox::get_line_edit() {
diff --git a/scene/gui/spin_box.h b/scene/gui/spin_box.h
index 3200480cf5..1b3dc9d79e 100644
--- a/scene/gui/spin_box.h
+++ b/scene/gui/spin_box.h
@@ -45,7 +45,7 @@ class SpinBox : public Range {
void _range_click_timeout();
void _text_entered(const String &p_string);
- virtual void _value_changed(double);
+ virtual void _value_changed(double) override;
String prefix;
String suffix;
@@ -73,7 +73,7 @@ protected:
public:
LineEdit *get_line_edit();
- virtual Size2 get_minimum_size() const;
+ virtual Size2 get_minimum_size() const override;
void set_align(LineEdit::Align p_align);
LineEdit::Align get_align() const;
diff --git a/scene/gui/split_container.h b/scene/gui/split_container.h
index 6dbd316a46..e345016f3d 100644
--- a/scene/gui/split_container.h
+++ b/scene/gui/split_container.h
@@ -75,9 +75,9 @@ public:
void set_dragger_visibility(DraggerVisibility p_visibility);
DraggerVisibility get_dragger_visibility() const;
- virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const;
+ virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override;
- virtual Size2 get_minimum_size() const;
+ virtual Size2 get_minimum_size() const override;
SplitContainer(bool p_vertical = false);
};
diff --git a/scene/gui/subviewport_container.h b/scene/gui/subviewport_container.h
index fc4c9f925a..e82ad772ce 100644
--- a/scene/gui/subviewport_container.h
+++ b/scene/gui/subviewport_container.h
@@ -52,7 +52,7 @@ public:
void set_stretch_shrink(int p_shrink);
int get_stretch_shrink() const;
- virtual Size2 get_minimum_size() const;
+ virtual Size2 get_minimum_size() const override;
SubViewportContainer();
};
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index 97b8362214..41b8fad49d 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -71,6 +71,8 @@ int TabContainer::_get_top_margin() const {
void TabContainer::_gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> mb = p_event;
+ Popup *popup = get_popup();
+
if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
Point2 pos(mb->get_position().x, mb->get_position().y);
Size2 size = get_size();
@@ -82,6 +84,7 @@ void TabContainer::_gui_input(const Ref<InputEvent> &p_event) {
// Handle menu button.
Ref<Texture2D> menu = get_theme_icon("menu");
+
if (popup && pos.x > size.width - menu->get_width()) {
emit_signal("pre_popup_pressed");
@@ -223,6 +226,7 @@ void TabContainer::_notification(int p_what) {
int header_width = get_size().width - side_margin * 2;
// Find the width of the header area.
+ Popup *popup = get_popup();
if (popup) {
header_width -= menu->get_width();
}
@@ -278,12 +282,13 @@ void TabContainer::_notification(int p_what) {
Color font_color_bg = get_theme_color("font_color_bg");
Color font_color_disabled = get_theme_color("font_color_disabled");
int side_margin = get_theme_constant("side_margin");
- int icon_text_distance = get_theme_constant("hseparation");
+ int icon_text_distance = get_theme_constant("icon_separation");
// Find out start and width of the header area.
int header_x = side_margin;
int header_width = size.width - side_margin * 2;
int header_height = _get_top_margin();
+ Popup *popup = get_popup();
if (popup) {
header_width -= menu->get_width();
}
@@ -435,7 +440,30 @@ void TabContainer::_notification(int p_what) {
void TabContainer::_on_theme_changed() {
if (get_tab_count() > 0) {
- set_current_tab(get_current_tab());
+ _repaint();
+ update();
+ }
+}
+
+void TabContainer::_repaint() {
+ Ref<StyleBox> sb = get_theme_stylebox("panel");
+ Vector<Control *> tabs = _get_tabs();
+ for (int i = 0; i < tabs.size(); i++) {
+ Control *c = tabs[i];
+ if (i == current) {
+ c->show();
+ c->set_anchors_and_margins_preset(Control::PRESET_WIDE);
+ if (tabs_visible) {
+ c->set_margin(MARGIN_TOP, _get_top_margin());
+ }
+ c->set_margin(Margin(MARGIN_TOP), c->get_margin(Margin(MARGIN_TOP)) + sb->get_margin(Margin(MARGIN_TOP)));
+ c->set_margin(Margin(MARGIN_LEFT), c->get_margin(Margin(MARGIN_LEFT)) + sb->get_margin(Margin(MARGIN_LEFT)));
+ c->set_margin(Margin(MARGIN_RIGHT), c->get_margin(Margin(MARGIN_RIGHT)) - sb->get_margin(Margin(MARGIN_RIGHT)));
+ c->set_margin(Margin(MARGIN_BOTTOM), c->get_margin(Margin(MARGIN_BOTTOM)) - sb->get_margin(Margin(MARGIN_BOTTOM)));
+
+ } else {
+ c->hide();
+ }
}
}
@@ -465,7 +493,7 @@ int TabContainer::_get_tab_width(int p_index) const {
if (icon.is_valid()) {
width += icon->get_width();
if (text != "") {
- width += get_theme_constant("hseparation");
+ width += get_theme_constant("icon_separation");
}
}
}
@@ -551,25 +579,7 @@ void TabContainer::set_current_tab(int p_current) {
int pending_previous = current;
current = p_current;
- Ref<StyleBox> sb = get_theme_stylebox("panel");
- Vector<Control *> tabs = _get_tabs();
- for (int i = 0; i < tabs.size(); i++) {
- Control *c = tabs[i];
- if (i == current) {
- c->show();
- c->set_anchors_and_margins_preset(Control::PRESET_WIDE);
- if (tabs_visible) {
- c->set_margin(MARGIN_TOP, _get_top_margin());
- }
- c->set_margin(Margin(MARGIN_TOP), c->get_margin(Margin(MARGIN_TOP)) + sb->get_margin(Margin(MARGIN_TOP)));
- c->set_margin(Margin(MARGIN_LEFT), c->get_margin(Margin(MARGIN_LEFT)) + sb->get_margin(Margin(MARGIN_LEFT)));
- c->set_margin(Margin(MARGIN_RIGHT), c->get_margin(Margin(MARGIN_RIGHT)) - sb->get_margin(Margin(MARGIN_RIGHT)));
- c->set_margin(Margin(MARGIN_BOTTOM), c->get_margin(Margin(MARGIN_BOTTOM)) - sb->get_margin(Margin(MARGIN_BOTTOM)));
-
- } else {
- c->hide();
- }
- }
+ _repaint();
_change_notify("current_tab");
@@ -744,6 +754,7 @@ int TabContainer::get_tab_idx_at_point(const Point2 &p_point) const {
Size2 size = get_size();
int right_ofs = 0;
+ Popup *popup = get_popup();
if (popup) {
Ref<Texture2D> menu = get_theme_icon("menu");
right_ofs += menu->get_width();
@@ -943,12 +954,24 @@ Size2 TabContainer::get_minimum_size() const {
void TabContainer::set_popup(Node *p_popup) {
ERR_FAIL_NULL(p_popup);
- popup = Object::cast_to<Popup>(p_popup);
+ Popup *popup = Object::cast_to<Popup>(p_popup);
+ popup_obj_id = popup ? popup->get_instance_id() : ObjectID();
update();
}
Popup *TabContainer::get_popup() const {
- return popup;
+ if (popup_obj_id.is_valid()) {
+ Popup *popup = Object::cast_to<Popup>(ObjectDB::get_instance(popup_obj_id));
+ if (popup) {
+ return popup;
+ } else {
+#ifdef DEBUG_ENABLED
+ ERR_PRINT("Popup assigned to TabContainer is gone!");
+#endif
+ popup_obj_id = ObjectID();
+ }
+ }
+ return nullptr;
}
void TabContainer::set_drag_to_rearrange_enabled(bool p_enabled) {
@@ -1032,7 +1055,6 @@ TabContainer::TabContainer() {
previous = 0;
align = ALIGN_CENTER;
tabs_visible = true;
- popup = nullptr;
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 e8cde74c83..7ea667d60f 100644
--- a/scene/gui/tab_container.h
+++ b/scene/gui/tab_container.h
@@ -57,7 +57,7 @@ private:
TabAlign align;
Control *_get_tab(int p_idx) const;
int _get_top_margin() const;
- Popup *popup;
+ mutable ObjectID popup_obj_id;
bool drag_to_rearrange_enabled;
bool use_hidden_tabs_for_min_size;
int tabs_rearrange_group;
@@ -65,6 +65,7 @@ private:
Vector<Control *> _get_tabs() const;
int _get_tab_width(int p_index) const;
void _on_theme_changed();
+ void _repaint();
void _on_mouse_exited();
void _update_current_tab();
@@ -72,12 +73,12 @@ protected:
void _child_renamed_callback();
void _gui_input(const Ref<InputEvent> &p_event);
void _notification(int p_what);
- virtual void add_child_notify(Node *p_child);
- virtual void remove_child_notify(Node *p_child);
+ virtual void add_child_notify(Node *p_child) override;
+ virtual void remove_child_notify(Node *p_child) override;
- Variant get_drag_data(const Point2 &p_point);
- bool can_drop_data(const Point2 &p_point, const Variant &p_data) const;
- void drop_data(const Point2 &p_point, const Variant &p_data);
+ Variant get_drag_data(const Point2 &p_point) override;
+ bool can_drop_data(const Point2 &p_point, const Variant &p_data) const override;
+ void drop_data(const Point2 &p_point, const Variant &p_data) override;
int get_tab_idx_at_point(const Point2 &p_point) const;
static void _bind_methods();
@@ -109,9 +110,9 @@ public:
Control *get_tab_control(int p_idx) const;
Control *get_current_tab_control() const;
- virtual Size2 get_minimum_size() const;
+ virtual Size2 get_minimum_size() const override;
- virtual void get_translatable_strings(List<String> *p_strings) const;
+ virtual void get_translatable_strings(List<String> *p_strings) const override;
void set_popup(Node *p_popup);
Popup *get_popup() const;
diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp
index 8f71aa7cab..d47f771d1d 100644
--- a/scene/gui/tabs.cpp
+++ b/scene/gui/tabs.cpp
@@ -388,6 +388,7 @@ void Tabs::set_current_tab(int p_current) {
}
ERR_FAIL_INDEX(p_current, get_tab_count());
+ previous = current;
current = p_current;
_change_notify("current_tab");
@@ -401,6 +402,10 @@ int Tabs::get_current_tab() const {
return current;
}
+int Tabs::get_previous_tab() const {
+ return previous;
+}
+
int Tabs::get_hovered_tab() const {
return hover;
}
@@ -588,6 +593,7 @@ void Tabs::add_tab(const String &p_str, const Ref<Texture2D> &p_icon) {
void Tabs::clear_tabs() {
tabs.clear();
current = 0;
+ previous = 0;
call_deferred("_update_hover");
update();
}
@@ -605,6 +611,7 @@ void Tabs::remove_tab(int p_idx) {
if (current < 0) {
current = 0;
+ previous = 0;
}
if (current >= tabs.size()) {
current = tabs.size() - 1;
@@ -917,6 +924,7 @@ void Tabs::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tab_count"), &Tabs::get_tab_count);
ClassDB::bind_method(D_METHOD("set_current_tab", "tab_idx"), &Tabs::set_current_tab);
ClassDB::bind_method(D_METHOD("get_current_tab"), &Tabs::get_current_tab);
+ ClassDB::bind_method(D_METHOD("get_previous_tab"), &Tabs::get_previous_tab);
ClassDB::bind_method(D_METHOD("set_tab_title", "tab_idx", "title"), &Tabs::set_tab_title);
ClassDB::bind_method(D_METHOD("get_tab_title", "tab_idx"), &Tabs::get_tab_title);
ClassDB::bind_method(D_METHOD("set_tab_icon", "tab_idx", "icon"), &Tabs::set_tab_icon);
@@ -970,6 +978,7 @@ void Tabs::_bind_methods() {
Tabs::Tabs() {
current = 0;
+ previous = 0;
tab_align = ALIGN_CENTER;
rb_hover = -1;
rb_pressing = false;
diff --git a/scene/gui/tabs.h b/scene/gui/tabs.h
index 8757f70ebe..b94c4a37a1 100644
--- a/scene/gui/tabs.h
+++ b/scene/gui/tabs.h
@@ -77,6 +77,7 @@ private:
bool missing_right;
Vector<Tab> tabs;
int current;
+ int previous;
int _get_top_margin() const;
TabAlign tab_align;
int rb_hover;
@@ -107,9 +108,9 @@ protected:
void _notification(int p_what);
static void _bind_methods();
- Variant get_drag_data(const Point2 &p_point);
- bool can_drop_data(const Point2 &p_point, const Variant &p_data) const;
- void drop_data(const Point2 &p_point, const Variant &p_data);
+ Variant get_drag_data(const Point2 &p_point) override;
+ bool can_drop_data(const Point2 &p_point, const Variant &p_data) const override;
+ void drop_data(const Point2 &p_point, const Variant &p_data) override;
int get_tab_idx_at_point(const Point2 &p_point) const;
public:
@@ -138,6 +139,7 @@ public:
int get_tab_count() const;
void set_current_tab(int p_current);
int get_current_tab() const;
+ int get_previous_tab() const;
int get_hovered_tab() const;
int get_tab_offset() const;
@@ -162,7 +164,7 @@ public:
void set_min_width(int p_width);
Rect2 get_tab_rect(int p_tab) const;
- Size2 get_minimum_size() const;
+ Size2 get_minimum_size() const override;
Tabs();
};
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 52aee8f089..957e1c11c7 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -44,31 +44,23 @@
#define TAB_PIXELS
-inline bool _is_symbol(CharType c) {
+inline bool _is_symbol(char32_t c) {
return is_symbol(c);
}
-static bool _is_text_char(CharType c) {
+static bool _is_text_char(char32_t c) {
return !is_symbol(c);
}
-static bool _is_whitespace(CharType c) {
+static bool _is_whitespace(char32_t c) {
return c == '\t' || c == ' ';
}
-static bool _is_char(CharType c) {
+static bool _is_char(char32_t c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
}
-static bool _is_number(CharType c) {
- return (c >= '0' && c <= '9');
-}
-
-static bool _is_hex_symbol(CharType c) {
- return ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
-}
-
-static bool _is_pair_right_symbol(CharType c) {
+static bool _is_pair_right_symbol(char32_t c) {
return c == '"' ||
c == '\'' ||
c == ')' ||
@@ -76,7 +68,7 @@ static bool _is_pair_right_symbol(CharType c) {
c == '}';
}
-static bool _is_pair_left_symbol(CharType c) {
+static bool _is_pair_left_symbol(char32_t c) {
return c == '"' ||
c == '\'' ||
c == '(' ||
@@ -84,11 +76,11 @@ static bool _is_pair_left_symbol(CharType c) {
c == '{';
}
-static bool _is_pair_symbol(CharType c) {
+static bool _is_pair_symbol(char32_t c) {
return _is_pair_left_symbol(c) || _is_pair_right_symbol(c);
}
-static CharType _get_right_pair_symbol(CharType c) {
+static char32_t _get_right_pair_symbol(char32_t c) {
if (c == '"') {
return '"';
}
@@ -115,6 +107,8 @@ static int _find_first_non_whitespace_column_of_line(const String &line) {
return left;
}
+///////////////////////////////////////////////////////////////////////////////
+
void TextEdit::Text::set_font(const Ref<Font> &p_font) {
font = p_font;
}
@@ -127,7 +121,7 @@ void TextEdit::Text::_update_line_cache(int p_line) const {
int w = 0;
int len = text[p_line].data.length();
- const CharType *str = text[p_line].data.c_str();
+ const char32_t *str = text[p_line].data.get_data();
// Update width.
@@ -136,94 +130,7 @@ void TextEdit::Text::_update_line_cache(int p_line) const {
}
text.write[p_line].width_cache = w;
-
text.write[p_line].wrap_amount_cache = -1;
-
- // Update regions.
-
- text.write[p_line].region_info.clear();
-
- for (int i = 0; i < len; i++) {
- if (!_is_symbol(str[i])) {
- continue;
- }
- if (str[i] == '\\') {
- i++; // Skip quoted anything.
- continue;
- }
-
- int left = len - i;
-
- for (int j = 0; j < color_regions->size(); j++) {
- const ColorRegion &cr = color_regions->operator[](j);
-
- /* BEGIN */
-
- int lr = cr.begin_key.length();
- const CharType *kc;
- bool match;
-
- if (lr != 0 && lr <= left) {
- kc = cr.begin_key.c_str();
-
- match = true;
-
- for (int k = 0; k < lr; k++) {
- if (kc[k] != str[i + k]) {
- match = false;
- break;
- }
- }
-
- if (match) {
- ColorRegionInfo cri;
- cri.end = false;
- cri.region = j;
- text.write[p_line].region_info[i] = cri;
- i += lr - 1;
-
- break;
- }
- }
-
- /* END */
-
- lr = cr.end_key.length();
- if (lr != 0 && lr <= left) {
- kc = cr.end_key.c_str();
-
- match = true;
-
- for (int k = 0; k < lr; k++) {
- if (kc[k] != str[i + k]) {
- match = false;
- break;
- }
- }
-
- if (match) {
- ColorRegionInfo cri;
- cri.end = true;
- cri.region = j;
- text.write[p_line].region_info[i] = cri;
- i += lr - 1;
-
- break;
- }
- }
- }
- }
-}
-
-const Map<int, TextEdit::Text::ColorRegionInfo> &TextEdit::Text::get_color_region_info(int p_line) const {
- static Map<int, ColorRegionInfo> cri;
- ERR_FAIL_INDEX_V(p_line, text.size(), cri);
-
- if (text[p_line].width_cache == -1) {
- _update_line_cache(p_line);
- }
-
- return text[p_line].region_info;
}
int TextEdit::Text::get_line_width(int p_line) const {
@@ -260,12 +167,6 @@ void TextEdit::Text::clear_wrap_cache() {
}
}
-void TextEdit::Text::clear_info_icons() {
- for (int i = 0; i < text.size(); i++) {
- text.write[i].has_info = false;
- }
-}
-
void TextEdit::Text::clear() {
text.clear();
insert(0, "");
@@ -293,12 +194,9 @@ void TextEdit::Text::set(int p_line, const String &p_text) {
void TextEdit::Text::insert(int p_at, const String &p_text) {
Line line;
+ line.gutters.resize(gutter_count);
line.marked = false;
- line.safe = false;
- line.breakpoint = false;
- line.bookmark = false;
line.hidden = false;
- line.has_info = false;
line.width_cache = -1;
line.wrap_amount_cache = -1;
line.data = p_text;
@@ -309,7 +207,7 @@ void TextEdit::Text::remove(int p_at) {
text.remove(p_at);
}
-int TextEdit::Text::get_char_width(CharType c, CharType next_c, int px) const {
+int TextEdit::Text::get_char_width(char32_t c, char32_t next_c, int px) const {
int tab_w = font->get_char_size(' ').width * indent_size;
int w = 0;
@@ -326,6 +224,32 @@ int TextEdit::Text::get_char_width(CharType c, CharType next_c, int px) const {
return w;
}
+void TextEdit::Text::add_gutter(int p_at) {
+ for (int i = 0; i < text.size(); i++) {
+ if (p_at < 0 || p_at > gutter_count) {
+ text.write[i].gutters.push_back(Gutter());
+ } else {
+ text.write[i].gutters.insert(p_at, Gutter());
+ }
+ }
+ gutter_count++;
+}
+
+void TextEdit::Text::remove_gutter(int p_gutter) {
+ for (int i = 0; i < text.size(); i++) {
+ text.write[i].gutters.remove(p_gutter);
+ }
+ gutter_count--;
+}
+
+void TextEdit::Text::move_gutters(int p_from_line, int p_to_line) {
+ text.write[p_to_line].gutters = text[p_from_line].gutters;
+ text.write[p_from_line].gutters.clear();
+ text.write[p_from_line].gutters.resize(gutter_count);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
void TextEdit::_update_scrollbars() {
Size2 size = get_size();
Size2 hmin = h_scroll->get_combined_minimum_size();
@@ -344,48 +268,15 @@ void TextEdit::_update_scrollbars() {
}
int visible_width = size.width - cache.style_normal->get_minimum_size().width;
- int total_width = text.get_max_width(true) + vmin.x;
-
- if (line_numbers) {
- total_width += cache.line_number_w;
- }
-
- if (draw_breakpoint_gutter || draw_bookmark_gutter) {
- total_width += cache.breakpoint_gutter_width;
- }
-
- if (draw_info_gutter) {
- total_width += cache.info_gutter_width;
- }
-
- if (draw_fold_gutter) {
- total_width += cache.fold_gutter_width;
- }
+ int total_width = text.get_max_width(true) + vmin.x + gutters_width + gutter_padding;
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) {
- use_hscroll = false;
- use_vscroll = false;
- } else {
- if (total_rows > visible_rows && total_width <= visible_width) {
- use_hscroll = false;
- }
-
- if (total_rows <= visible_rows && total_width > visible_width) {
- use_vscroll = false;
- }
- }
-
updating_scrolls = true;
- if (use_vscroll) {
+ if (total_rows > visible_rows) {
v_scroll->show();
v_scroll->set_max(total_rows + get_visible_rows_offset());
v_scroll->set_page(visible_rows + get_visible_rows_offset());
@@ -403,7 +294,7 @@ void TextEdit::_update_scrollbars() {
v_scroll->hide();
}
- if (use_hscroll && !is_wrap_enabled()) {
+ if (total_width > visible_width && !is_wrap_enabled()) {
h_scroll->show();
h_scroll->set_max(total_width);
h_scroll->set_page(visible_width);
@@ -618,14 +509,13 @@ void TextEdit::_notification(int p_what) {
case NOTIFICATION_THEME_CHANGED: {
_update_caches();
_update_wrap_at();
- syntax_highlighting_cache.clear();
} break;
- case NOTIFICATION_WM_FOCUS_IN: {
+ case NOTIFICATION_WM_WINDOW_FOCUS_IN: {
window_has_focus = true;
draw_caret = true;
update();
} break;
- case NOTIFICATION_WM_FOCUS_OUT: {
+ case NOTIFICATION_WM_WINDOW_FOCUS_OUT: {
window_has_focus = false;
draw_caret = false;
update();
@@ -657,30 +547,17 @@ void TextEdit::_notification(int p_what) {
adjust_viewport_to_cursor();
first_draw = false;
}
- Size2 size = get_size();
- if ((!has_focus() && !menu->has_focus()) || !window_has_focus) {
- draw_caret = false;
- }
- if (draw_breakpoint_gutter || draw_bookmark_gutter) {
- breakpoint_gutter_width = (get_row_height() * 55) / 100;
- cache.breakpoint_gutter_width = breakpoint_gutter_width;
- } else {
- cache.breakpoint_gutter_width = 0;
- }
-
- if (draw_info_gutter) {
- info_gutter_width = (get_row_height());
- cache.info_gutter_width = info_gutter_width;
- } else {
- cache.info_gutter_width = 0;
+ /* Prevent the resource getting lost between the editor and game. */
+ if (Engine::get_singleton()->is_editor_hint()) {
+ if (syntax_highlighter.is_valid() && syntax_highlighter->get_text_edit() != this) {
+ syntax_highlighter->set_text_edit(this);
+ }
}
- if (draw_fold_gutter) {
- fold_gutter_width = (get_row_height() * 55) / 100;
- cache.fold_gutter_width = fold_gutter_width;
- } else {
- cache.fold_gutter_width = 0;
+ Size2 size = get_size();
+ if ((!has_focus() && !menu->has_focus()) || !window_has_focus) {
+ draw_caret = false;
}
cache.minimap_width = 0;
@@ -688,28 +565,11 @@ void TextEdit::_notification(int p_what) {
cache.minimap_width = minimap_width;
}
- int line_number_char_count = 0;
-
- {
- int lc = text.size();
- cache.line_number_w = 0;
- while (lc) {
- cache.line_number_w += 1;
- lc /= 10;
- };
-
- if (line_numbers) {
- line_number_char_count = cache.line_number_w;
- cache.line_number_w = (cache.line_number_w + 1) * cache.font->get_char_size('0').width;
- } else {
- cache.line_number_w = 0;
- }
- }
_update_scrollbars();
RID ci = get_canvas_item();
RenderingServer::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_beg = cache.style_normal->get_margin(MARGIN_LEFT) + gutters_width + gutter_padding;
int xmargin_end = size.width - cache.style_normal->get_margin(MARGIN_RIGHT) - cache.minimap_width;
// Let's do it easy for now.
@@ -728,10 +588,8 @@ void TextEdit::_notification(int p_what) {
Color color = readonly ? cache.font_color_readonly : cache.font_color;
- if (syntax_coloring) {
- if (cache.background_color.a > 0.01) {
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(), get_size()), cache.background_color);
- }
+ if (cache.background_color.a > 0.01) {
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(), get_size()), cache.background_color);
}
if (line_length_guidelines) {
@@ -761,8 +619,8 @@ 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.
- CharType c = text[cursor.line][cursor.column];
- CharType closec = 0;
+ char32_t c = text[cursor.line][cursor.column];
+ char32_t closec = 0;
if (c == '[') {
closec = ']';
@@ -778,10 +636,10 @@ void TextEdit::_notification(int p_what) {
for (int i = cursor.line; i < text.size(); i++) {
int from = i == cursor.line ? cursor.column + 1 : 0;
for (int j = from; j < text[i].length(); j++) {
- CharType cc = text[i][j];
+ char32_t cc = text[i][j];
// Ignore any brackets inside a string.
if (cc == '"' || cc == '\'') {
- CharType quotation = cc;
+ char32_t quotation = cc;
do {
j++;
if (!(j < text[i].length())) {
@@ -827,8 +685,8 @@ void TextEdit::_notification(int p_what) {
}
if (cursor.column > 0) {
- CharType c = text[cursor.line][cursor.column - 1];
- CharType closec = 0;
+ char32_t c = text[cursor.line][cursor.column - 1];
+ char32_t closec = 0;
if (c == ']') {
closec = '[';
@@ -844,10 +702,10 @@ void TextEdit::_notification(int p_what) {
for (int i = cursor.line; i >= 0; i--) {
int from = i == cursor.line ? cursor.column - 2 : text[i].length() - 1;
for (int j = from; j >= 0; j--) {
- CharType cc = text[i][j];
+ char32_t cc = text[i][j];
// Ignore any brackets inside a string.
if (cc == '"' || cc == '\'') {
- CharType quotation = cc;
+ char32_t quotation = cc;
do {
j--;
if (!(j >= 0)) {
@@ -902,8 +760,6 @@ void TextEdit::_notification(int p_what) {
// 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" : " ";
-
int cursor_wrap_index = get_cursor_wrap_index();
FontDrawer drawer(cache.font, Color(1, 1, 1));
@@ -954,10 +810,7 @@ void TextEdit::_notification(int p_what) {
break;
}
- Map<int, HighlighterInfo> color_map;
- if (syntax_coloring) {
- color_map = _get_line_syntax_highlighting(minimap_line);
- }
+ Dictionary color_map = _get_line_syntax_highlighting(minimap_line);
Color current_color = cache.font_color;
if (readonly) {
@@ -995,15 +848,13 @@ void TextEdit::_notification(int p_what) {
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;
- }
+ if (color_map.has(last_wrap_column + j)) {
+ current_color = color_map[last_wrap_column + j].get("color");
+ if (readonly) {
+ current_color.a = cache.font_color_readonly.a;
}
- color = current_color;
}
+ color = current_color;
if (j == 0) {
previous_color = color;
@@ -1077,10 +928,8 @@ void TextEdit::_notification(int p_what) {
const String &fullstr = text[line];
- Map<int, HighlighterInfo> color_map;
- if (syntax_coloring) {
- color_map = _get_line_syntax_highlighting(line);
- }
+ Dictionary color_map = _get_line_syntax_highlighting(line);
+
// Ensure we at least use the font color.
Color current_color = readonly ? cache.font_color_readonly : cache.font_color;
@@ -1121,9 +970,7 @@ void TextEdit::_notification(int p_what) {
int ofs_y = (i * get_row_height() + cache.line_spacing / 2) + ofs_readonly;
ofs_y -= cursor.wrap_ofs * get_row_height();
- if (smooth_scroll_enabled) {
- ofs_y += (-get_v_scroll_offset()) * get_row_height();
- }
+ ofs_y -= get_v_scroll_offset() * get_row_height();
// Check if line contains highlighted word.
int highlighted_text_col = -1;
@@ -1169,118 +1016,65 @@ void TextEdit::_notification(int p_what) {
if (line_wrap_index == 0) {
// 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
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y + get_row_height() - EDSCALE, xmargin_end - xmargin_beg, EDSCALE), cache.breakpoint_color);
-#else
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, xmargin_end - xmargin_beg, get_row_height()), cache.breakpoint_color);
-#endif
- }
+ int gutter_offset = cache.style_normal->get_margin(MARGIN_LEFT);
+ for (int g = 0; g < gutters.size(); g++) {
+ const GutterInfo gutter = gutters[g];
- // Draw bookmark marker.
- if (text.is_bookmark(line)) {
- if (draw_bookmark_gutter) {
- int vertical_gap = (get_row_height() * 40) / 100;
- int horizontal_gap = (cache.breakpoint_gutter_width * 30) / 100;
- int marker_radius = get_row_height() - (vertical_gap * 2);
- RenderingServer::get_singleton()->canvas_item_add_circle(ci, Point2(cache.style_normal->get_margin(MARGIN_LEFT) + horizontal_gap - 2 + marker_radius / 2, ofs_y + vertical_gap + marker_radius / 2), marker_radius, Color(cache.bookmark_color.r, cache.bookmark_color.g, cache.bookmark_color.b));
+ if (!gutter.draw || gutter.width <= 0) {
+ continue;
}
- }
- // 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.
- RenderingServer::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));
- }
- }
+ switch (gutter.type) {
+ case GUTTER_TYPE_STRING: {
+ const String &text = get_line_gutter_text(line, g);
+ if (text == "") {
+ break;
+ }
- // 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<Texture2D> info_icon = text.get_info_icon(line);
- // 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;
- }
- if (icon_size.height > get_row_height() - horizontal_gap) {
- icon_size.height = get_row_height() - horizontal_gap;
- }
+ int yofs = ofs_y + (get_row_height() - cache.font->get_height()) / 2;
+ cache.font->draw(ci, Point2(gutter_offset + ofs_x, yofs + cache.font->get_ascent()), text, get_line_gutter_item_color(line, g));
+ } break;
+ case GUTTER_TPYE_ICON: {
+ const Ref<Texture2D> icon = get_line_gutter_icon(line, g);
+ if (icon.is_null()) {
+ break;
+ }
- Size2i icon_pos;
- int xofs = horizontal_gap - (info_icon->get_width() / 4);
- int yofs = vertical_gap - (info_icon->get_height() / 4);
- icon_pos.x = gutter_left + xofs + ofs_x;
- icon_pos.y = ofs_y + yofs;
+ Rect2i gutter_rect = Rect2i(Point2i(gutter_offset, ofs_y), Size2i(gutter.width, get_row_height()));
- draw_texture_rect(info_icon, Rect2(icon_pos, icon_size));
- }
+ int horizontal_padding = gutter_rect.size.x / 6;
+ int vertical_padding = gutter_rect.size.y / 6;
- // Draw execution marker.
- if (executing_line == line) {
- if (draw_breakpoint_gutter) {
- int icon_extra_size = 4;
- 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) + icon_extra_size;
- int marker_width = cache.breakpoint_gutter_width - (horizontal_gap * 2) + icon_extra_size;
- cache.executing_icon->draw_rect(ci, Rect2(cache.style_normal->get_margin(MARGIN_LEFT) + horizontal_gap - 2 - icon_extra_size / 2, ofs_y + vertical_gap - icon_extra_size / 2, marker_width, marker_height), false, Color(cache.executing_line_color.r, cache.executing_line_color.g, cache.executing_line_color.b));
- } else {
-#ifdef TOOLS_ENABLED
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y + get_row_height() - EDSCALE, xmargin_end - xmargin_beg, EDSCALE), cache.executing_line_color);
-#else
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, xmargin_end - xmargin_beg, get_row_height()), cache.executing_line_color);
-#endif
- }
- }
+ gutter_rect.position += Point2(horizontal_padding, vertical_padding);
+ gutter_rect.size -= Point2(horizontal_padding, vertical_padding) * 2;
- // 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;
- if (is_folded(line)) {
- int xofs = horizontal_gap - (cache.can_fold_icon->get_width()) / 2;
- int yofs = (get_row_height() - cache.folded_icon->get_height()) / 2;
- cache.folded_icon->draw(ci, Point2(gutter_left + xofs + ofs_x, ofs_y + yofs), cache.code_folding_color);
- } else if (can_fold(line)) {
- int xofs = -cache.can_fold_icon->get_width() / 2 - horizontal_gap + 3;
- int yofs = (get_row_height() - cache.can_fold_icon->get_height()) / 2;
- cache.can_fold_icon->draw(ci, Point2(gutter_left + xofs + ofs_x, ofs_y + yofs), cache.code_folding_color);
- }
- }
-
- // Draw line numbers.
- if (cache.line_number_w) {
- int yofs = ofs_y + (get_row_height() - cache.font->get_height()) / 2;
- String fc = String::num(line + 1);
- while (fc.length() < line_number_char_count) {
- fc = line_num_padding + fc;
+ icon->draw_rect(ci, gutter_rect, false, get_line_gutter_item_color(line, g));
+ } break;
+ case GUTTER_TPYE_CUSTOM: {
+ if (gutter.custom_draw_obj.is_valid()) {
+ Object *cdo = ObjectDB::get_instance(gutter.custom_draw_obj);
+ if (cdo) {
+ Rect2i gutter_rect = Rect2i(Point2i(gutter_offset, ofs_y), Size2i(gutter.width, get_row_height()));
+ cdo->call(gutter.custom_draw_callback, line, g, Rect2(gutter_rect));
+ }
+ }
+ } break;
}
- cache.font->draw(ci, Point2(cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + cache.info_gutter_width + ofs_x, yofs + cache.font->get_ascent()), fc, text.is_safe(line) ? cache.safe_line_number_color : cache.line_number_color);
+ gutter_offset += gutter.width;
}
}
// Loop through characters in one line.
int j = 0;
for (; 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) {
- current_color.a = cache.font_color_readonly.a;
- }
+ if (color_map.has(last_wrap_column + j)) {
+ current_color = color_map[last_wrap_column + j].get("color");
+ if (readonly && current_color.a > cache.font_color_readonly.a) {
+ current_color.a = cache.font_color_readonly.a;
}
- color = current_color;
}
+ color = current_color;
int char_w;
@@ -1421,8 +1215,8 @@ void TextEdit::_notification(int p_what) {
break;
}
- CharType cchar = ime_text[ofs];
- CharType next = ime_text[ofs + 1];
+ char32_t cchar = ime_text[ofs];
+ char32_t next = ime_text[ofs + 1];
int im_char_width = cache.font->get_char_size(cchar, next).width;
if ((char_ofs + char_margin + im_char_width) >= xmargin_end) {
@@ -1466,7 +1260,7 @@ void TextEdit::_notification(int p_what) {
if (cursor.column == last_wrap_column + j && cursor.line == line && cursor_wrap_index == line_wrap_index && block_caret && draw_caret && !insert_mode) {
color = cache.caret_background_color;
- } else if (!syntax_coloring && block_caret) {
+ } else if (block_caret) {
color = readonly ? cache.font_color_readonly : cache.font_color;
}
@@ -1517,8 +1311,8 @@ void TextEdit::_notification(int p_what) {
break;
}
- CharType cchar = ime_text[ofs];
- CharType next = ime_text[ofs + 1];
+ char32_t cchar = ime_text[ofs];
+ char32_t next = ime_text[ofs + 1];
int im_char_width = cache.font->get_char_size(cchar, next).width;
if ((char_ofs + char_margin + im_char_width) >= xmargin_end) {
@@ -1628,12 +1422,6 @@ void TextEdit::_notification(int p_what) {
for (int i = 0; i < lines; i++) {
int l = line_from + i;
ERR_CONTINUE(l < 0 || l >= completion_options_size);
- Color text_color = cache.completion_font_color;
- for (int j = 0; j < color_regions.size(); j++) {
- if (completion_options[l].insert_text.begins_with(color_regions[j].begin_key)) {
- text_color = color_regions[j].color;
- }
- }
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);
@@ -1649,7 +1437,7 @@ 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 - (icon_area_size.x + icon_hsep));
+ draw_string(cache.font, title_pos, completion_options[l].display, completion_options[l].font_color, completion_rect.size.width - (icon_area_size.x + icon_hsep));
}
if (scrollw) {
@@ -1755,8 +1543,8 @@ void TextEdit::_notification(int p_what) {
DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + cursor_pos, get_viewport()->get_window_id());
}
- if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD)) {
- DisplayServer::get_singleton()->virtual_keyboard_show(get_text(), get_global_rect());
+ if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) {
+ DisplayServer::get_singleton()->virtual_keyboard_show(get_text(), get_global_rect(), true);
}
} break;
case NOTIFICATION_FOCUS_EXIT: {
@@ -1771,7 +1559,7 @@ void TextEdit::_notification(int p_what) {
ime_text = "";
ime_selection = Point2();
- if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD)) {
+ if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) {
DisplayServer::get_singleton()->virtual_keyboard_hide();
}
} break;
@@ -1785,12 +1573,12 @@ void TextEdit::_notification(int p_what) {
}
}
-void TextEdit::_consume_pair_symbol(CharType ch) {
+void TextEdit::_consume_pair_symbol(char32_t ch) {
int cursor_position_to_move = cursor_get_column() + 1;
- CharType ch_single[2] = { ch, 0 };
- CharType ch_single_pair[2] = { _get_right_pair_symbol(ch), 0 };
- CharType ch_pair[3] = { ch, _get_right_pair_symbol(ch), 0 };
+ char32_t ch_single[2] = { ch, 0 };
+ char32_t ch_single_pair[2] = { _get_right_pair_symbol(ch), 0 };
+ char32_t ch_pair[3] = { ch, _get_right_pair_symbol(ch), 0 };
if (is_selection_active()) {
int new_column, new_line;
@@ -1895,8 +1683,8 @@ void TextEdit::_consume_backspace_for_pair_symbol(int prev_line, int prev_column
bool remove_right_symbol = false;
if (cursor.column < text[cursor.line].length() && cursor.column > 0) {
- CharType left_char = text[cursor.line][cursor.column - 1];
- CharType right_char = text[cursor.line][cursor.column];
+ char32_t left_char = text[cursor.line][cursor.column - 1];
+ char32_t right_char = text[cursor.line][cursor.column];
if (right_char == _get_right_pair_symbol(left_char)) {
remove_right_symbol = true;
@@ -1921,18 +1709,34 @@ void TextEdit::backspace_at_cursor() {
int prev_line = cursor.column ? cursor.line : cursor.line - 1;
int prev_column = cursor.column ? (cursor.column - 1) : (text[cursor.line - 1].length());
- if (is_line_hidden(cursor.line)) {
- set_line_as_hidden(prev_line, true);
- }
- if (is_line_set_as_breakpoint(cursor.line)) {
- if (!text.is_breakpoint(prev_line)) {
- emit_signal("breakpoint_toggled", prev_line);
+ if (cursor.line != prev_line) {
+ for (int i = 0; i < gutters.size(); i++) {
+ if (!gutters[i].overwritable) {
+ continue;
+ }
+
+ if (text.get_line_gutter_text(cursor.line, i) != "") {
+ text.set_line_gutter_text(prev_line, i, text.get_line_gutter_text(cursor.line, i));
+ text.set_line_gutter_item_color(prev_line, i, text.get_line_gutter_item_color(cursor.line, i));
+ }
+
+ if (text.get_line_gutter_icon(cursor.line, i).is_valid()) {
+ text.set_line_gutter_icon(prev_line, i, text.get_line_gutter_icon(cursor.line, i));
+ text.set_line_gutter_item_color(prev_line, i, text.get_line_gutter_item_color(cursor.line, i));
+ }
+
+ if (text.get_line_gutter_metadata(cursor.line, i) != "") {
+ text.set_line_gutter_metadata(prev_line, i, text.get_line_gutter_metadata(cursor.line, i));
+ }
+
+ if (text.is_line_gutter_clickable(cursor.line, i)) {
+ text.set_line_gutter_clickable(prev_line, i, true);
+ }
}
- set_line_as_breakpoint(prev_line, true);
}
- if (text.has_info_icon(cursor.line)) {
- set_line_info_icon(prev_line, text.get_info_icon(cursor.line), text.get_info(cursor.line));
+ if (is_line_hidden(cursor.line)) {
+ set_line_as_hidden(prev_line, true);
}
if (auto_brace_completion_enabled &&
@@ -1996,6 +1800,9 @@ void TextEdit::indent_right() {
for (int i = start_line; i <= end_line; i++) {
String line_text = get_line(i);
+ if (line_text.size() == 0 && is_selection_active()) {
+ continue;
+ }
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.
int left = _find_first_non_whitespace_column_of_line(line_text);
@@ -2119,7 +1926,7 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co
row = text.size() - 1;
col = text[row].size();
} else {
- int colx = p_mouse.x - (cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width + cache.info_gutter_width);
+ int colx = p_mouse.x - (cache.style_normal->get_margin(MARGIN_LEFT) + gutters_width + gutter_padding);
colx += cursor.x_ofs;
col = get_char_pos_for_line(colx, row, wrap_index);
if (is_wrap_enabled() && wrap_index < times_line_wraps(row)) {
@@ -2164,7 +1971,7 @@ Vector2i TextEdit::_get_cursor_pixel_pos() {
// 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 x = cache.style_normal->get_margin(MARGIN_LEFT) + gutters_width + gutter_padding - cursor.x_ofs;
int ix = 0;
while (ix < rows2[0].size() && ix < cursor.column) {
if (cache.font != nullptr) {
@@ -2276,14 +2083,14 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
if (mb->get_button_index() == BUTTON_WHEEL_UP && !mb->get_command()) {
if (mb->get_shift()) {
h_scroll->set_value(h_scroll->get_value() - (100 * mb->get_factor()));
- } else {
+ } else if (v_scroll->is_visible()) {
_scroll_up(3 * mb->get_factor());
}
}
if (mb->get_button_index() == BUTTON_WHEEL_DOWN && !mb->get_command()) {
if (mb->get_shift()) {
h_scroll->set_value(h_scroll->get_value() + (100 * mb->get_factor()));
- } else {
+ } else if (v_scroll->is_visible()) {
_scroll_down(3 * mb->get_factor());
}
}
@@ -2299,45 +2106,24 @@ 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);
- // 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) {
- set_line_as_breakpoint(row, !is_line_set_as_breakpoint(row));
- emit_signal("breakpoint_toggled", row);
- return;
+ int left_margin = cache.style_normal->get_margin(MARGIN_LEFT);
+ for (int i = 0; i < gutters.size(); i++) {
+ if (!gutters[i].draw || gutters[i].width <= 0) {
+ continue;
}
- }
- // 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;
- if (mb->get_position().x > gutter_left - 6 && mb->get_position().x <= gutter_left + cache.info_gutter_width - 3) {
- emit_signal("info_clicked", row, text.get_info(row));
+ if (mb->get_position().x > left_margin && mb->get_position().x <= (left_margin + gutters[i].width) - 3) {
+ emit_signal("gutter_clicked", row, i);
return;
}
- }
- // Toggle fold on gutter click if can.
- if (draw_fold_gutter) {
- int left_margin = cache.style_normal->get_margin(MARGIN_LEFT);
- int gutter_left = left_margin + cache.breakpoint_gutter_width + cache.line_number_w + cache.info_gutter_width;
- if (mb->get_position().x > gutter_left - 6 && mb->get_position().x <= gutter_left + cache.fold_gutter_width - 3) {
- if (is_folded(row)) {
- unfold_line(row);
- } else if (can_fold(row)) {
- fold_line(row);
- }
- return;
- }
+ left_margin += gutters[i].width;
}
// 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;
- if (mb->get_position().x > line_width - 3 && mb->get_position().x <= line_width + cache.folded_eol_icon->get_width() + 3) {
+ left_margin += gutter_padding + text.get_line_width(row) - cursor.x_ofs;
+ if (mb->get_position().x > left_margin && mb->get_position().x <= left_margin + cache.folded_eol_icon->get_width() + 3) {
unfold_line(row);
return;
}
@@ -2661,7 +2447,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
if (k->get_unicode() > 32) {
_reset_caret_blink_timer();
- const CharType chr[2] = { (CharType)k->get_unicode(), 0 };
+ const char32_t chr[2] = { (char32_t)k->get_unicode(), 0 };
if (auto_brace_completion_enabled && _is_pair_symbol(chr[0])) {
_consume_pair_symbol(chr[0]);
} else {
@@ -2870,7 +2656,6 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
// Indent once again if previous line will end with ':','{','[','(' and the line is not a comment
// (i.e. colon/brace precedes current cursor position).
if (cursor.column > 0) {
- const Map<int, Text::ColorRegionInfo> &cri_map = text.get_color_region_info(cursor.line);
bool indent_char_found = false;
bool should_indent = false;
char indent_char = ':';
@@ -2889,7 +2674,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
continue;
}
- if (indent_char_found && cri_map.has(i) && (color_regions[cri_map[i].region].begin_key == "#" || color_regions[cri_map[i].region].begin_key == "//")) {
+ if (indent_char_found && is_line_comment(i)) {
should_indent = true;
break;
} else if (indent_char_found && !_is_whitespace(c)) {
@@ -2906,7 +2691,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
}
// No need to move the brace below if we are not taking the text with us.
- char closing_char = _get_right_pair_symbol(indent_char);
+ char32_t closing_char = _get_right_pair_symbol(indent_char);
if ((closing_char != 0) && (closing_char == text[cursor.line][cursor.column]) && !k->get_command()) {
brace_indent = true;
ins += "\n" + ins.substr(1, ins.length() - 2);
@@ -3434,7 +3219,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
// 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];
+ char32_t c = text[cursor.line][current_line_whitespace_len];
if (c != '\t' && c != ' ') {
break;
}
@@ -3580,7 +3365,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
int current_line_whitespace_len = 0;
while (current_line_whitespace_len < text[cursor.line].length()) {
- CharType c = text[cursor.line][current_line_whitespace_len];
+ char32_t c = text[cursor.line][current_line_whitespace_len];
if (c != '\t' && c != ' ')
break;
current_line_whitespace_len++;
@@ -3729,7 +3514,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
return;
}
- if (!keycode_handled && !k->get_command()) { // For German keyboards.
+ if (!keycode_handled && (!k->get_command() || (k->get_command() && k->get_alt()))) { // For German keyboards.
if (k->get_unicode() >= 32) {
if (readonly) {
@@ -3746,7 +3531,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
}
}
- const CharType chr[2] = { (CharType)k->get_unicode(), 0 };
+ const char32_t chr[2] = { (char32_t)k->get_unicode(), 0 };
if (completion_hint != "" && k->get_unicode() == ')') {
completion_hint = "";
@@ -3897,30 +3682,15 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i
Vector<String> substrings = p_text.replace("\r", "").split("\n");
- /* STEP 2: Fire breakpoint_toggled signals. */
-
// Is this just a new empty line?
bool shift_first_line = p_char == 0 && p_text.replace("\r", "") == "\n";
- int i = p_line + !shift_first_line;
- int lines = substrings.size() - 1;
- for (; i < text.size(); i++) {
- if (text.is_breakpoint(i)) {
- if ((i - lines < p_line || !text.is_breakpoint(i - lines)) || (i - lines == p_line && !shift_first_line)) {
- emit_signal("breakpoint_toggled", i);
- }
- if (i + lines >= text.size() || !text.is_breakpoint(i + lines)) {
- emit_signal("breakpoint_toggled", i + lines);
- }
- }
- }
-
- /* STEP 3: Add spaces if the char is greater than the end of the line. */
+ /* STEP 2: 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 3: 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());
@@ -3940,15 +3710,10 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i
}
if (shift_first_line) {
- text.set_breakpoint(p_line + 1, text.is_breakpoint(p_line));
+ text.move_gutters(p_line, p_line + 1);
text.set_hidden(p_line + 1, text.is_hidden(p_line));
- if (text.has_info_icon(p_line)) {
- text.set_info_icon(p_line + 1, text.get_info_icon(p_line), text.get_info(p_line));
- }
- text.set_breakpoint(p_line, false);
text.set_hidden(p_line, false);
- text.set_info_icon(p_line, nullptr, "");
}
text.set_line_wrap_amount(p_line, -1);
@@ -3962,7 +3727,7 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i
}
text_changed_dirty = true;
}
- _line_edited_from(p_line);
+ emit_signal("lines_edited_from", p_line, r_end_line);
}
String TextEdit::_base_get_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column) const {
@@ -3999,19 +3764,6 @@ void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_li
String pre_text = text[p_from_line].substr(0, p_from_column);
String post_text = text[p_to_line].substr(p_to_column, text[p_to_line].length());
- int lines = p_to_line - p_from_line;
-
- for (int i = p_from_line + 1; i < text.size(); i++) {
- if (text.is_breakpoint(i)) {
- if (i + lines >= text.size() || !text.is_breakpoint(i + lines)) {
- emit_signal("breakpoint_toggled", i);
- }
- if (i > p_to_line && (i - lines < 0 || !text.is_breakpoint(i - lines))) {
- emit_signal("breakpoint_toggled", i - lines);
- }
- }
- }
-
for (int i = p_from_line; i < p_to_line; i++) {
text.remove(p_from_line + 1);
}
@@ -4025,7 +3777,7 @@ void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_li
}
text_changed_dirty = true;
}
- _line_edited_from(p_from_line);
+ emit_signal("lines_edited_from", p_to_line, p_from_line);
}
void TextEdit::_insert_text(int p_line, int p_char, const String &p_text, int *r_end_line, int *r_end_char) {
@@ -4145,22 +3897,6 @@ void TextEdit::_insert_text_at_cursor(const String &p_text) {
update();
}
-void TextEdit::_line_edited_from(int p_line) {
- int cache_size = color_region_cache.size();
- 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() {
int totalsize = 0;
@@ -4235,7 +3971,7 @@ 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 - cache.minimap_width - wrap_right_offset;
+ wrap_at = get_size().width - cache.style_normal->get_minimum_size().width - gutters_width - gutter_padding - cache.minimap_width - wrap_right_offset;
update_cursor_wrap_offset();
text.clear_wrap_cache();
@@ -4270,7 +4006,7 @@ void TextEdit::adjust_viewport_to_cursor() {
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 - cache.minimap_width;
+ int visible_width = get_size().width - cache.style_normal->get_minimum_size().width - gutters_width - gutter_padding - cache.minimap_width;
if (v_scroll->is_visible_in_tree()) {
visible_width -= v_scroll->get_combined_minimum_size().width;
}
@@ -4305,7 +4041,7 @@ void TextEdit::center_viewport_to_cursor() {
}
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 - cache.minimap_width;
+ int visible_width = get_size().width - cache.style_normal->get_minimum_size().width - gutters_width - gutter_padding - cache.minimap_width;
if (v_scroll->is_visible_in_tree()) {
visible_width -= v_scroll->get_combined_minimum_size().width;
}
@@ -4389,7 +4125,7 @@ Vector<String> TextEdit::get_wrap_rows_text(int p_line) const {
}
while (col < line_text.length()) {
- CharType c = line_text[col];
+ char32_t c = line_text[col];
int w = text.get_char_width(c, line_text[col + 1], px + word_px);
int indent_ofs = (cur_wrap_index != 0 ? tab_offset_px : 0);
@@ -4752,54 +4488,41 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const {
return CURSOR_POINTING_HAND;
}
- int gutter = cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width + cache.info_gutter_width;
if ((completion_active && completion_rect.has_point(p_pos))) {
return CURSOR_ARROW;
}
- if (p_pos.x < gutter) {
- int row, col;
- _get_mouse_pos(p_pos, row, col);
- int left_margin = cache.style_normal->get_margin(MARGIN_LEFT);
- // 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;
- }
+ int row, col;
+ _get_mouse_pos(p_pos, row, col);
- // 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)) {
- return CURSOR_POINTING_HAND;
+ int left_margin = cache.style_normal->get_margin(MARGIN_LEFT);
+ int gutter = left_margin + gutters_width;
+ if (p_pos.x < gutter) {
+ for (int i = 0; i < gutters.size(); i++) {
+ if (!gutters[i].draw) {
+ continue;
}
- return CURSOR_ARROW;
- }
- // 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;
- } else {
- return CURSOR_ARROW;
+ if (p_pos.x > left_margin && p_pos.x <= (left_margin + gutters[i].width) - 3) {
+ if (gutters[i].clickable || is_line_gutter_clickable(row, i)) {
+ return CURSOR_POINTING_HAND;
+ }
}
+ left_margin += gutters[i].width;
}
+ return CURSOR_ARROW;
+ }
+ 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;
- } 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.
- 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;
- if (p_pos.x > line_width - 3 && p_pos.x <= line_width + cache.folded_eol_icon->get_width() + 3) {
- return CURSOR_POINTING_HAND;
- }
+ }
+
+ // EOL fold icon.
+ if (is_folded(row)) {
+ gutter += gutter_padding + text.get_line_width(row) - cursor.x_ofs;
+ if (p_pos.x > gutter - 3 && p_pos.x <= gutter + cache.folded_eol_icon->get_width() + 3) {
+ return CURSOR_POINTING_HAND;
}
}
@@ -4999,28 +4722,18 @@ void TextEdit::_update_caches() {
cache.font = get_theme_font("font");
cache.caret_color = get_theme_color("caret_color");
cache.caret_background_color = get_theme_color("caret_background_color");
- cache.line_number_color = get_theme_color("line_number_color");
- cache.safe_line_number_color = get_theme_color("safe_line_number_color");
cache.font_color = get_theme_color("font_color");
cache.font_color_selected = get_theme_color("font_color_selected");
cache.font_color_readonly = get_theme_color("font_color_readonly");
- cache.keyword_color = get_theme_color("keyword_color");
- cache.function_color = get_theme_color("function_color");
- cache.member_variable_color = get_theme_color("member_variable_color");
- cache.number_color = get_theme_color("number_color");
cache.selection_color = get_theme_color("selection_color");
cache.mark_color = get_theme_color("mark_color");
cache.current_line_color = get_theme_color("current_line_color");
cache.line_length_guideline_color = get_theme_color("line_length_guideline_color");
- cache.bookmark_color = get_theme_color("bookmark_color");
- cache.breakpoint_color = get_theme_color("breakpoint_color");
- cache.executing_line_color = get_theme_color("executing_line_color");
cache.code_folding_color = get_theme_color("code_folding_color");
cache.brace_mismatch_color = get_theme_color("brace_mismatch_color");
cache.word_highlighted_color = get_theme_color("word_highlighted_color");
cache.search_result_color = get_theme_color("search_result_color");
cache.search_result_border_color = get_theme_color("search_result_border_color");
- cache.symbol_color = get_theme_color("symbol_color");
cache.background_color = get_theme_color("background_color");
#ifdef TOOLS_ENABLED
cache.line_spacing = get_theme_constant("line_spacing") * EDSCALE;
@@ -5030,147 +4743,215 @@ void TextEdit::_update_caches() {
cache.row_height = cache.font->get_height() + cache.line_spacing;
cache.tab_icon = get_theme_icon("tab");
cache.space_icon = get_theme_icon("space");
- cache.folded_icon = get_theme_icon("folded");
- cache.can_fold_icon = get_theme_icon("fold");
cache.folded_eol_icon = get_theme_icon("GuiEllipsis", "EditorIcons");
- cache.executing_icon = get_theme_icon("MainPlay", "EditorIcons");
text.set_font(cache.font);
+ text.clear_width_cache();
- if (syntax_highlighter) {
- syntax_highlighter->_update_cache();
+ if (syntax_highlighter.is_valid()) {
+ syntax_highlighter->set_text_edit(this);
}
}
-SyntaxHighlighter *TextEdit::_get_syntax_highlighting() {
+/* Syntax Highlighting. */
+Ref<SyntaxHighlighter> TextEdit::get_syntax_highlighter() {
return syntax_highlighter;
}
-void TextEdit::_set_syntax_highlighting(SyntaxHighlighter *p_syntax_highlighter) {
+void TextEdit::set_syntax_highlighter(Ref<SyntaxHighlighter> p_syntax_highlighter) {
syntax_highlighter = p_syntax_highlighter;
- if (syntax_highlighter) {
- syntax_highlighter->set_text_editor(this);
- syntax_highlighter->_update_cache();
+ if (syntax_highlighter.is_valid()) {
+ syntax_highlighter->set_text_edit(this);
}
- syntax_highlighting_cache.clear();
update();
}
-int TextEdit::_is_line_in_region(int p_line) {
- // Do we have in cache?
- if (color_region_cache.has(p_line)) {
- return color_region_cache[p_line];
+/* Gutters. */
+void TextEdit::_update_gutter_width() {
+ gutters_width = 0;
+ for (int i = 0; i < gutters.size(); i++) {
+ if (gutters[i].draw) {
+ gutters_width += gutters[i].width;
+ }
+ }
+ if (gutters_width > 0) {
+ gutter_padding = 2;
}
+ update();
+}
- // 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)) {
- break;
- }
+void TextEdit::add_gutter(int p_at) {
+ if (p_at < 0 || p_at > gutters.size()) {
+ gutters.push_back(GutterInfo());
+ } else {
+ gutters.insert(p_at, GutterInfo());
}
- // 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;
+ for (int i = 0; i < text.size() + 1; i++) {
+ text.add_gutter(p_at);
}
- for (int i = previous_line; i < p_line; i++) {
- const Map<int, Text::ColorRegionInfo> &cri_map = _get_line_color_region_info(i);
- for (const Map<int, Text::ColorRegionInfo>::Element *E = cri_map.front(); E; E = E->next()) {
- const Text::ColorRegionInfo &cri = E->get();
- if (in_region == -1) {
- if (!cri.end) {
- in_region = cri.region;
- }
- } else if (in_region == cri.region && !_get_color_region(cri.region).line_only) {
- if (cri.end || _get_color_region(cri.region).eq) {
- in_region = -1;
- }
- }
- }
+ emit_signal("gutter_added");
+ update();
+}
- if (in_region >= 0 && _get_color_region(in_region).line_only) {
- in_region = -1;
- }
+void TextEdit::remove_gutter(int p_gutter) {
+ ERR_FAIL_INDEX(p_gutter, gutters.size());
+
+ gutters.remove(p_gutter);
- color_region_cache[i + 1] = in_region;
+ for (int i = 0; i < text.size() + 1; i++) {
+ text.remove_gutter(p_gutter);
}
- return in_region;
+ emit_signal("gutter_removed");
+ update();
}
-TextEdit::ColorRegion TextEdit::_get_color_region(int p_region) const {
- if (p_region < 0 || p_region >= color_regions.size()) {
- return ColorRegion();
- }
- return color_regions[p_region];
+int TextEdit::get_gutter_count() const {
+ return gutters.size();
}
-Map<int, TextEdit::Text::ColorRegionInfo> TextEdit::_get_line_color_region_info(int p_line) const {
- if (p_line < 0 || p_line > text.size() - 1) {
- return Map<int, Text::ColorRegionInfo>();
- }
- return text.get_color_region_info(p_line);
+void TextEdit::set_gutter_name(int p_gutter, const String &p_name) {
+ ERR_FAIL_INDEX(p_gutter, gutters.size());
+ gutters.write[p_gutter].name = p_name;
}
-void TextEdit::clear_colors() {
- keywords.clear();
- member_keywords.clear();
- color_regions.clear();
- color_region_cache.clear();
- syntax_highlighting_cache.clear();
- text.clear_width_cache();
- update();
+String TextEdit::get_gutter_name(int p_gutter) const {
+ ERR_FAIL_INDEX_V(p_gutter, gutters.size(), "");
+ return gutters[p_gutter].name;
}
-void TextEdit::add_keyword_color(const String &p_keyword, const Color &p_color) {
- keywords[p_keyword] = p_color;
- syntax_highlighting_cache.clear();
+void TextEdit::set_gutter_type(int p_gutter, GutterType p_type) {
+ ERR_FAIL_INDEX(p_gutter, gutters.size());
+ gutters.write[p_gutter].type = p_type;
update();
}
-bool TextEdit::has_keyword_color(String p_keyword) const {
- return keywords.has(p_keyword);
+TextEdit::GutterType TextEdit::get_gutter_type(int p_gutter) const {
+ ERR_FAIL_INDEX_V(p_gutter, gutters.size(), GUTTER_TYPE_STRING);
+ return gutters[p_gutter].type;
}
-Color TextEdit::get_keyword_color(String p_keyword) const {
- ERR_FAIL_COND_V(!keywords.has(p_keyword), Color());
- return keywords[p_keyword];
+void TextEdit::set_gutter_width(int p_gutter, int p_width) {
+ ERR_FAIL_INDEX(p_gutter, gutters.size());
+ gutters.write[p_gutter].width = p_width;
+ _update_gutter_width();
}
-void TextEdit::add_color_region(const String &p_begin_key, const String &p_end_key, const Color &p_color, bool p_line_only) {
- color_regions.push_back(ColorRegion(p_begin_key, p_end_key, p_color, p_line_only));
- syntax_highlighting_cache.clear();
- text.clear_width_cache();
+int TextEdit::get_gutter_width(int p_gutter) const {
+ ERR_FAIL_INDEX_V(p_gutter, gutters.size(), -1);
+ return gutters[p_gutter].width;
+}
+
+void TextEdit::set_gutter_draw(int p_gutter, bool p_draw) {
+ ERR_FAIL_INDEX(p_gutter, gutters.size());
+ gutters.write[p_gutter].draw = p_draw;
+ _update_gutter_width();
+}
+
+bool TextEdit::is_gutter_drawn(int p_gutter) const {
+ ERR_FAIL_INDEX_V(p_gutter, gutters.size(), false);
+ return gutters[p_gutter].draw;
+}
+
+void TextEdit::set_gutter_clickable(int p_gutter, bool p_clickable) {
+ ERR_FAIL_INDEX(p_gutter, gutters.size());
+ gutters.write[p_gutter].clickable = p_clickable;
update();
}
-void TextEdit::add_member_keyword(const String &p_keyword, const Color &p_color) {
- member_keywords[p_keyword] = p_color;
- syntax_highlighting_cache.clear();
+bool TextEdit::is_gutter_clickable(int p_gutter) const {
+ ERR_FAIL_INDEX_V(p_gutter, gutters.size(), false);
+ return gutters[p_gutter].clickable;
+}
+
+void TextEdit::set_gutter_overwritable(int p_gutter, bool p_overwritable) {
+ ERR_FAIL_INDEX(p_gutter, gutters.size());
+ gutters.write[p_gutter].overwritable = p_overwritable;
+}
+
+bool TextEdit::is_gutter_overwritable(int p_gutter) const {
+ ERR_FAIL_INDEX_V(p_gutter, gutters.size(), false);
+ return gutters[p_gutter].overwritable;
+}
+
+void TextEdit::set_gutter_custom_draw(int p_gutter, Object *p_object, const StringName &p_callback) {
+ ERR_FAIL_INDEX(p_gutter, gutters.size());
+ ERR_FAIL_NULL(p_object);
+
+ gutters.write[p_gutter].custom_draw_obj = p_object->get_instance_id();
+ gutters.write[p_gutter].custom_draw_callback = p_callback;
update();
}
-bool TextEdit::has_member_color(String p_member) const {
- return member_keywords.has(p_member);
+// Line gutters.
+void TextEdit::set_line_gutter_metadata(int p_line, int p_gutter, const Variant &p_metadata) {
+ ERR_FAIL_INDEX(p_line, text.size());
+ ERR_FAIL_INDEX(p_gutter, gutters.size());
+ text.set_line_gutter_metadata(p_line, p_gutter, p_metadata);
}
-Color TextEdit::get_member_color(String p_member) const {
- return member_keywords[p_member];
+Variant TextEdit::get_line_gutter_metadata(int p_line, int p_gutter) const {
+ ERR_FAIL_INDEX_V(p_line, text.size(), "");
+ ERR_FAIL_INDEX_V(p_gutter, gutters.size(), "");
+ return text.get_line_gutter_metadata(p_line, p_gutter);
}
-void TextEdit::clear_member_keywords() {
- member_keywords.clear();
- syntax_highlighting_cache.clear();
+void TextEdit::set_line_gutter_text(int p_line, int p_gutter, const String &p_text) {
+ ERR_FAIL_INDEX(p_line, text.size());
+ ERR_FAIL_INDEX(p_gutter, gutters.size());
+ text.set_line_gutter_text(p_line, p_gutter, p_text);
update();
}
-void TextEdit::set_syntax_coloring(bool p_enabled) {
- syntax_coloring = p_enabled;
+String TextEdit::get_line_gutter_text(int p_line, int p_gutter) const {
+ ERR_FAIL_INDEX_V(p_line, text.size(), "");
+ ERR_FAIL_INDEX_V(p_gutter, gutters.size(), "");
+ return text.get_line_gutter_text(p_line, p_gutter);
+}
+
+void TextEdit::set_line_gutter_icon(int p_line, int p_gutter, Ref<Texture2D> p_icon) {
+ ERR_FAIL_INDEX(p_line, text.size());
+ ERR_FAIL_INDEX(p_gutter, gutters.size());
+ text.set_line_gutter_icon(p_line, p_gutter, p_icon);
+ update();
+}
+
+Ref<Texture2D> TextEdit::get_line_gutter_icon(int p_line, int p_gutter) const {
+ ERR_FAIL_INDEX_V(p_line, text.size(), Ref<Texture2D>());
+ ERR_FAIL_INDEX_V(p_gutter, gutters.size(), Ref<Texture2D>());
+ return text.get_line_gutter_icon(p_line, p_gutter);
+}
+
+void TextEdit::set_line_gutter_item_color(int p_line, int p_gutter, const Color &p_color) {
+ ERR_FAIL_INDEX(p_line, text.size());
+ ERR_FAIL_INDEX(p_gutter, gutters.size());
+ text.set_line_gutter_item_color(p_line, p_gutter, p_color);
update();
}
-bool TextEdit::is_syntax_coloring_enabled() const {
- return syntax_coloring;
+Color TextEdit::get_line_gutter_item_color(int p_line, int p_gutter) {
+ ERR_FAIL_INDEX_V(p_line, text.size(), Color());
+ ERR_FAIL_INDEX_V(p_gutter, gutters.size(), Color());
+ return text.get_line_gutter_item_color(p_line, p_gutter);
+}
+
+void TextEdit::set_line_gutter_clickable(int p_line, int p_gutter, bool p_clickable) {
+ ERR_FAIL_INDEX(p_line, text.size());
+ ERR_FAIL_INDEX(p_gutter, gutters.size());
+ text.set_line_gutter_clickable(p_line, p_gutter, p_clickable);
+}
+
+bool TextEdit::is_line_gutter_clickable(int p_line, int p_gutter) const {
+ ERR_FAIL_INDEX_V(p_line, text.size(), false);
+ ERR_FAIL_INDEX_V(p_gutter, gutters.size(), false);
+ return text.is_line_gutter_clickable(p_line, p_gutter);
+}
+
+void TextEdit::add_keyword(const String &p_keyword) {
+ keywords.insert(p_keyword);
+}
+
+void TextEdit::clear_keywords() {
+ keywords.clear();
}
void TextEdit::set_auto_indent(bool p_auto_indent) {
@@ -5449,17 +5230,16 @@ int TextEdit::_get_column_pos_of_word(const String &p_key, const String &p_searc
return col;
}
-Vector<int> TextEdit::_search_bind(const String &p_key, uint32_t p_search_flags, int p_from_line, int p_from_column) const {
+Dictionary TextEdit::_search_bind(const String &p_key, uint32_t p_search_flags, int p_from_line, int p_from_column) const {
int col, line;
if (search(p_key, p_search_flags, p_from_line, p_from_column, line, col)) {
- Vector<int> result;
- result.resize(2);
- result.set(SEARCH_RESULT_COLUMN, col);
- result.set(SEARCH_RESULT_LINE, line);
+ Dictionary result;
+ result["line"] = line;
+ result["column"] = col;
return result;
} else {
- return Vector<int>();
+ return Dictionary();
}
}
@@ -5596,106 +5376,6 @@ void TextEdit::set_line_as_marked(int p_line, bool p_marked) {
update();
}
-void TextEdit::set_line_as_safe(int p_line, bool p_safe) {
- ERR_FAIL_INDEX(p_line, text.size());
- text.set_safe(p_line, p_safe);
- update();
-}
-
-bool TextEdit::is_line_set_as_safe(int p_line) const {
- ERR_FAIL_INDEX_V(p_line, text.size(), false);
- return text.is_safe(p_line);
-}
-
-void TextEdit::set_executing_line(int p_line) {
- ERR_FAIL_INDEX(p_line, text.size());
- executing_line = p_line;
- update();
-}
-
-void TextEdit::clear_executing_line() {
- executing_line = -1;
- update();
-}
-
-bool TextEdit::is_line_set_as_bookmark(int p_line) const {
- ERR_FAIL_INDEX_V(p_line, text.size(), false);
- return text.is_bookmark(p_line);
-}
-
-void TextEdit::set_line_as_bookmark(int p_line, bool p_bookmark) {
- ERR_FAIL_INDEX(p_line, text.size());
- text.set_bookmark(p_line, p_bookmark);
- update();
-}
-
-void TextEdit::get_bookmarks(List<int> *p_bookmarks) const {
- for (int i = 0; i < text.size(); i++) {
- if (text.is_bookmark(i)) {
- p_bookmarks->push_back(i);
- }
- }
-}
-
-Array TextEdit::get_bookmarks_array() const {
- Array arr;
- for (int i = 0; i < text.size(); i++) {
- if (text.is_bookmark(i)) {
- arr.append(i);
- }
- }
- return arr;
-}
-
-bool TextEdit::is_line_set_as_breakpoint(int p_line) const {
- ERR_FAIL_INDEX_V(p_line, text.size(), false);
- return text.is_breakpoint(p_line);
-}
-
-void TextEdit::set_line_as_breakpoint(int p_line, bool p_breakpoint) {
- ERR_FAIL_INDEX(p_line, text.size());
- text.set_breakpoint(p_line, p_breakpoint);
- update();
-}
-
-void TextEdit::get_breakpoints(List<int> *p_breakpoints) const {
- for (int i = 0; i < text.size(); i++) {
- if (text.is_breakpoint(i)) {
- p_breakpoints->push_back(i);
- }
- }
-}
-
-Array TextEdit::get_breakpoints_array() const {
- Array arr;
- for (int i = 0; i < text.size(); i++) {
- if (text.is_breakpoint(i)) {
- arr.append(i);
- }
- }
- return arr;
-}
-
-void TextEdit::remove_breakpoints() {
- for (int i = 0; i < text.size(); i++) {
- if (text.is_breakpoint(i)) {
- /* Should "breakpoint_toggled" be fired when breakpoints are removed this way? */
- text.set_breakpoint(i, false);
- }
- }
-}
-
-void TextEdit::set_line_info_icon(int p_line, Ref<Texture2D> p_icon, String p_info) {
- ERR_FAIL_INDEX(p_line, text.size());
- text.set_info_icon(p_line, p_icon, p_info);
- update();
-}
-
-void TextEdit::clear_info_icons() {
- text.clear_info_icons();
- update();
-}
-
void TextEdit::set_line_as_hidden(int p_line, bool p_hidden) {
ERR_FAIL_INDEX(p_line, text.size());
if (is_hiding_enabled() || !p_hidden) {
@@ -5847,18 +5527,19 @@ bool TextEdit::is_line_comment(int p_line) const {
// 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);
-
int line_length = text[p_line].size();
for (int i = 0; i < line_length - 1; i++) {
- if (_is_symbol(text[p_line][i]) && cri_map.has(i)) {
- const Text::ColorRegionInfo &cri = cri_map[i];
- return color_regions[cri.region].begin_key == "#" || color_regions[cri.region].begin_key == "//";
- } else if (_is_whitespace(text[p_line][i])) {
+ if (_is_whitespace(text[p_line][i])) {
continue;
- } else {
- break;
}
+ if (_is_symbol(text[p_line][i])) {
+ if (text[p_line][i] == '\\') {
+ i++; // Skip quoted anything.
+ continue;
+ }
+ return text[p_line][i] == '#' || (i + 1 < line_length && text[p_line][i] == '/' && text[p_line][i + 1] == '/');
+ }
+ break;
}
return false;
}
@@ -6369,9 +6050,9 @@ void TextEdit::_confirm_completion() {
// When inserted into the middle of an existing string/method, don't add an unnecessary quote/bracket.
String line = text[cursor.line];
- CharType next_char = line[cursor.column];
- CharType last_completion_char = completion_current.insert_text[completion_current.insert_text.length() - 1];
- CharType last_completion_char_display = completion_current.display[completion_current.display.length() - 1];
+ char32_t next_char = line[cursor.column];
+ char32_t last_completion_char = completion_current.insert_text[completion_current.insert_text.length() - 1];
+ char32_t last_completion_char_display = completion_current.display[completion_current.display.length() - 1];
if ((last_completion_char == '"' || last_completion_char == '\'') && (last_completion_char == next_char || last_completion_char_display == next_char)) {
_remove_text(cursor.line, cursor.column, cursor.line, cursor.column + 1);
@@ -6415,7 +6096,7 @@ void TextEdit::_cancel_completion() {
update();
}
-static bool _is_completable(CharType c) {
+static bool _is_completable(char32_t c) {
return !_is_symbol(c) || c == '"' || c == '\'';
}
@@ -6546,14 +6227,14 @@ void TextEdit::_update_completion_candidates() {
String display_lower = option.display.to_lower();
- const CharType *ssq = &s[0];
- const CharType *ssq_lower = &s_lower[0];
+ const char32_t *ssq = &s[0];
+ const char32_t *ssq_lower = &s_lower[0];
- const CharType *tgt = &option.display[0];
- const CharType *tgt_lower = &display_lower[0];
+ const char32_t *tgt = &option.display[0];
+ const char32_t *tgt_lower = &display_lower[0];
- const CharType *ssq_last_tgt = nullptr;
- const CharType *ssq_lower_last_tgt = nullptr;
+ const char32_t *ssq_last_tgt = nullptr;
+ const char32_t *ssq_lower_last_tgt = nullptr;
for (; *tgt; tgt++, tgt_lower++) {
if (*ssq == *tgt) {
@@ -6670,7 +6351,7 @@ String TextEdit::get_word_at_pos(const Vector2 &p_pos) const {
int beg, end;
if (select_word(s, col, beg, end)) {
bool inside_quotes = false;
- CharType selected_quote = '\0';
+ char32_t selected_quote = '\0';
int qbegin = 0, qend = 0;
for (int i = 0; i < s.length(); i++) {
if (s[i] == '"' || s[i] == '\'') {
@@ -6756,20 +6437,6 @@ void TextEdit::insert_at(const String &p_text, int at) {
}
}
-void TextEdit::set_show_line_numbers(bool p_show) {
- line_numbers = p_show;
- update();
-}
-
-void TextEdit::set_line_numbers_zero_padded(bool p_zero_padded) {
- line_numbers_zero_padded = p_zero_padded;
- update();
-}
-
-bool TextEdit::is_show_line_numbers_enabled() const {
- return line_numbers;
-}
-
void TextEdit::set_show_line_length_guidelines(bool p_show) {
line_length_guidelines = p_show;
update();
@@ -6785,69 +6452,6 @@ void TextEdit::set_line_length_guideline_hard_column(int p_column) {
update();
}
-void TextEdit::set_bookmark_gutter_enabled(bool p_draw) {
- draw_bookmark_gutter = p_draw;
- update();
-}
-
-bool TextEdit::is_bookmark_gutter_enabled() const {
- return draw_bookmark_gutter;
-}
-
-void TextEdit::set_breakpoint_gutter_enabled(bool p_draw) {
- draw_breakpoint_gutter = p_draw;
- update();
-}
-
-bool TextEdit::is_breakpoint_gutter_enabled() const {
- return draw_breakpoint_gutter;
-}
-
-void TextEdit::set_breakpoint_gutter_width(int p_gutter_width) {
- breakpoint_gutter_width = p_gutter_width;
- update();
-}
-
-int TextEdit::get_breakpoint_gutter_width() const {
- return cache.breakpoint_gutter_width;
-}
-
-void TextEdit::set_draw_fold_gutter(bool p_draw) {
- draw_fold_gutter = p_draw;
- update();
-}
-
-bool TextEdit::is_drawing_fold_gutter() const {
- return draw_fold_gutter;
-}
-
-void TextEdit::set_fold_gutter_width(int p_gutter_width) {
- fold_gutter_width = p_gutter_width;
- update();
-}
-
-int TextEdit::get_fold_gutter_width() const {
- return cache.fold_gutter_width;
-}
-
-void TextEdit::set_draw_info_gutter(bool p_draw) {
- draw_info_gutter = p_draw;
- update();
-}
-
-bool TextEdit::is_drawing_info_gutter() const {
- return draw_info_gutter;
-}
-
-void TextEdit::set_info_gutter_width(int p_gutter_width) {
- info_gutter_width = p_gutter_width;
- update();
-}
-
-int TextEdit::get_info_gutter_width() const {
- return info_gutter_width;
-}
-
void TextEdit::set_draw_minimap(bool p_draw) {
draw_minimap = p_draw;
update();
@@ -6950,6 +6554,10 @@ void TextEdit::set_shortcut_keys_enabled(bool p_enabled) {
_generate_context_menu();
}
+void TextEdit::set_virtual_keyboard_enabled(bool p_enable) {
+ virtual_keyboard_enabled = p_enable;
+}
+
void TextEdit::set_selecting_enabled(bool p_enabled) {
selecting_enabled = p_enabled;
@@ -6968,6 +6576,10 @@ bool TextEdit::is_shortcut_keys_enabled() const {
return shortcut_keys_enabled;
}
+bool TextEdit::is_virtual_keyboard_enabled() const {
+ return virtual_keyboard_enabled;
+}
+
PopupMenu *TextEdit::get_menu() const {
return menu;
}
@@ -6982,9 +6594,6 @@ void TextEdit::_bind_methods() {
BIND_ENUM_CONSTANT(SEARCH_WHOLE_WORDS);
BIND_ENUM_CONSTANT(SEARCH_BACKWARDS);
- BIND_ENUM_CONSTANT(SEARCH_RESULT_COLUMN);
- BIND_ENUM_CONSTANT(SEARCH_RESULT_LINE);
-
/*
ClassDB::bind_method(D_METHOD("delete_char"),&TextEdit::delete_char);
ClassDB::bind_method(D_METHOD("delete_line"),&TextEdit::delete_line);
@@ -7023,6 +6632,8 @@ void TextEdit::_bind_methods() {
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_virtual_keyboard_enabled", "enable"), &TextEdit::set_virtual_keyboard_enabled);
+ ClassDB::bind_method(D_METHOD("is_virtual_keyboard_enabled"), &TextEdit::is_virtual_keyboard_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);
@@ -7047,16 +6658,10 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("redo"), &TextEdit::redo);
ClassDB::bind_method(D_METHOD("clear_undo_history"), &TextEdit::clear_undo_history);
- ClassDB::bind_method(D_METHOD("set_show_line_numbers", "enable"), &TextEdit::set_show_line_numbers);
- ClassDB::bind_method(D_METHOD("is_show_line_numbers_enabled"), &TextEdit::is_show_line_numbers_enabled);
ClassDB::bind_method(D_METHOD("set_draw_tabs"), &TextEdit::set_draw_tabs);
ClassDB::bind_method(D_METHOD("is_drawing_tabs"), &TextEdit::is_drawing_tabs);
ClassDB::bind_method(D_METHOD("set_draw_spaces"), &TextEdit::set_draw_spaces);
ClassDB::bind_method(D_METHOD("is_drawing_spaces"), &TextEdit::is_drawing_spaces);
- ClassDB::bind_method(D_METHOD("set_breakpoint_gutter_enabled", "enable"), &TextEdit::set_breakpoint_gutter_enabled);
- ClassDB::bind_method(D_METHOD("is_breakpoint_gutter_enabled"), &TextEdit::is_breakpoint_gutter_enabled);
- ClassDB::bind_method(D_METHOD("set_draw_fold_gutter"), &TextEdit::set_draw_fold_gutter);
- ClassDB::bind_method(D_METHOD("is_drawing_fold_gutter"), &TextEdit::is_drawing_fold_gutter);
ClassDB::bind_method(D_METHOD("set_hiding_enabled", "enable"), &TextEdit::set_hiding_enabled);
ClassDB::bind_method(D_METHOD("is_hiding_enabled"), &TextEdit::is_hiding_enabled);
@@ -7076,8 +6681,42 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_override_selected_font_color", "override"), &TextEdit::set_override_selected_font_color);
ClassDB::bind_method(D_METHOD("is_overriding_selected_font_color"), &TextEdit::is_overriding_selected_font_color);
- ClassDB::bind_method(D_METHOD("set_syntax_coloring", "enable"), &TextEdit::set_syntax_coloring);
- ClassDB::bind_method(D_METHOD("is_syntax_coloring_enabled"), &TextEdit::is_syntax_coloring_enabled);
+ ClassDB::bind_method(D_METHOD("set_syntax_highlighter", "syntax_highlighter"), &TextEdit::set_syntax_highlighter);
+ ClassDB::bind_method(D_METHOD("get_syntax_highlighter"), &TextEdit::get_syntax_highlighter);
+
+ /* Gutters. */
+ BIND_ENUM_CONSTANT(GUTTER_TYPE_STRING);
+ BIND_ENUM_CONSTANT(GUTTER_TPYE_ICON);
+ BIND_ENUM_CONSTANT(GUTTER_TPYE_CUSTOM);
+
+ ClassDB::bind_method(D_METHOD("add_gutter", "at"), &TextEdit::add_gutter, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("remove_gutter", "gutter"), &TextEdit::remove_gutter);
+ ClassDB::bind_method(D_METHOD("get_gutter_count"), &TextEdit::get_gutter_count);
+ ClassDB::bind_method(D_METHOD("set_gutter_name", "gutter", "name"), &TextEdit::set_gutter_name);
+ ClassDB::bind_method(D_METHOD("get_gutter_name", "gutter"), &TextEdit::get_gutter_name);
+ ClassDB::bind_method(D_METHOD("set_gutter_type", "gutter", "type"), &TextEdit::set_gutter_type);
+ ClassDB::bind_method(D_METHOD("get_gutter_type", "gutter"), &TextEdit::get_gutter_type);
+ ClassDB::bind_method(D_METHOD("set_gutter_width", "gutter", "width"), &TextEdit::set_gutter_width);
+ ClassDB::bind_method(D_METHOD("get_gutter_width", "gutter"), &TextEdit::get_gutter_width);
+ ClassDB::bind_method(D_METHOD("set_gutter_draw", "gutter", "draw"), &TextEdit::set_gutter_draw);
+ ClassDB::bind_method(D_METHOD("is_gutter_drawn", "gutter"), &TextEdit::is_gutter_drawn);
+ ClassDB::bind_method(D_METHOD("set_gutter_clickable", "gutter", "clickable"), &TextEdit::set_gutter_clickable);
+ ClassDB::bind_method(D_METHOD("is_gutter_clickable", "gutter"), &TextEdit::is_gutter_clickable);
+ ClassDB::bind_method(D_METHOD("set_gutter_overwritable", "gutter", "overwritable"), &TextEdit::set_gutter_overwritable);
+ ClassDB::bind_method(D_METHOD("is_gutter_overwritable", "gutter"), &TextEdit::is_gutter_overwritable);
+ ClassDB::bind_method(D_METHOD("set_gutter_custom_draw", "column", "object", "callback"), &TextEdit::set_gutter_custom_draw);
+
+ // Line gutters.
+ ClassDB::bind_method(D_METHOD("set_line_gutter_metadata", "line", "gutter", "metadata"), &TextEdit::set_line_gutter_metadata);
+ ClassDB::bind_method(D_METHOD("get_line_gutter_metadata", "line", "gutter"), &TextEdit::get_line_gutter_metadata);
+ ClassDB::bind_method(D_METHOD("set_line_gutter_text", "line", "gutter", "text"), &TextEdit::set_line_gutter_text);
+ ClassDB::bind_method(D_METHOD("get_line_gutter_text", "line", "gutter"), &TextEdit::get_line_gutter_text);
+ ClassDB::bind_method(D_METHOD("set_line_gutter_icon", "line", "gutter", "icon"), &TextEdit::set_line_gutter_icon);
+ ClassDB::bind_method(D_METHOD("get_line_gutter_icon", "line", "gutter"), &TextEdit::get_line_gutter_icon);
+ ClassDB::bind_method(D_METHOD("set_line_gutter_item_color", "line", "gutter", "color"), &TextEdit::set_line_gutter_item_color);
+ ClassDB::bind_method(D_METHOD("get_line_gutter_item_color", "line", "gutter"), &TextEdit::get_line_gutter_item_color);
+ ClassDB::bind_method(D_METHOD("set_line_gutter_clickable", "line", "gutter", "clickable"), &TextEdit::set_line_gutter_clickable);
+ ClassDB::bind_method(D_METHOD("is_line_gutter_clickable", "line", "gutter"), &TextEdit::is_line_gutter_clickable);
ClassDB::bind_method(D_METHOD("set_highlight_current_line", "enabled"), &TextEdit::set_highlight_current_line);
ClassDB::bind_method(D_METHOD("is_highlight_current_line_enabled"), &TextEdit::is_highlight_current_line_enabled);
@@ -7091,17 +6730,9 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_h_scroll", "value"), &TextEdit::set_h_scroll);
ClassDB::bind_method(D_METHOD("get_h_scroll"), &TextEdit::get_h_scroll);
- ClassDB::bind_method(D_METHOD("add_keyword_color", "keyword", "color"), &TextEdit::add_keyword_color);
- ClassDB::bind_method(D_METHOD("has_keyword_color", "keyword"), &TextEdit::has_keyword_color);
- ClassDB::bind_method(D_METHOD("get_keyword_color", "keyword"), &TextEdit::get_keyword_color);
- ClassDB::bind_method(D_METHOD("add_color_region", "begin_key", "end_key", "color", "line_only"), &TextEdit::add_color_region, DEFVAL(false));
- ClassDB::bind_method(D_METHOD("clear_colors"), &TextEdit::clear_colors);
ClassDB::bind_method(D_METHOD("menu_option", "option"), &TextEdit::menu_option);
ClassDB::bind_method(D_METHOD("get_menu"), &TextEdit::get_menu);
- ClassDB::bind_method(D_METHOD("get_breakpoints"), &TextEdit::get_breakpoints_array);
- ClassDB::bind_method(D_METHOD("remove_breakpoints"), &TextEdit::remove_breakpoints);
-
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);
@@ -7110,16 +6741,13 @@ void TextEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "readonly"), "set_readonly", "is_readonly");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_current_line"), "set_highlight_current_line", "is_highlight_current_line_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "syntax_highlighting"), "set_syntax_coloring", "is_syntax_coloring_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_line_numbers"), "set_show_line_numbers", "is_show_line_numbers_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_tabs"), "set_draw_tabs", "is_drawing_tabs");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_spaces"), "set_draw_spaces", "is_drawing_spaces");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "breakpoint_gutter"), "set_breakpoint_gutter_enabled", "is_breakpoint_gutter_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fold_gutter"), "set_draw_fold_gutter", "is_drawing_fold_gutter");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_all_occurrences"), "set_highlight_all_occurrences", "is_highlight_all_occurrences_enabled");
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, "virtual_keyboard_enabled"), "set_virtual_keyboard_enabled", "is_virtual_keyboard_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::FLOAT, "v_scroll_speed"), "set_v_scroll_speed", "get_v_scroll_speed");
@@ -7128,6 +6756,8 @@ void TextEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scroll_vertical"), "set_v_scroll", "get_v_scroll");
ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_horizontal"), "set_h_scroll", "get_h_scroll");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "syntax_highlighter", PROPERTY_HINT_RESOURCE_TYPE, "SyntaxHighlighter", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE), "set_syntax_highlighter", "get_syntax_highlighter");
+
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");
@@ -7140,10 +6770,12 @@ void TextEdit::_bind_methods() {
ADD_SIGNAL(MethodInfo("cursor_changed"));
ADD_SIGNAL(MethodInfo("text_changed"));
+ ADD_SIGNAL(MethodInfo("lines_edited_from", PropertyInfo(Variant::INT, "from_line"), PropertyInfo(Variant::INT, "to_line")));
ADD_SIGNAL(MethodInfo("request_completion"));
- ADD_SIGNAL(MethodInfo("breakpoint_toggled", PropertyInfo(Variant::INT, "row")));
+ ADD_SIGNAL(MethodInfo("gutter_clicked", PropertyInfo(Variant::INT, "line"), PropertyInfo(Variant::INT, "gutter")));
+ ADD_SIGNAL(MethodInfo("gutter_added"));
+ ADD_SIGNAL(MethodInfo("gutter_removed"));
ADD_SIGNAL(MethodInfo("symbol_lookup", PropertyInfo(Variant::STRING, "symbol"), PropertyInfo(Variant::INT, "row"), PropertyInfo(Variant::INT, "column")));
- ADD_SIGNAL(MethodInfo("info_clicked", PropertyInfo(Variant::INT, "row"), PropertyInfo(Variant::STRING, "info")));
ADD_SIGNAL(MethodInfo("symbol_validate", PropertyInfo(Variant::STRING, "symbol")));
BIND_ENUM_CONSTANT(MENU_CUT);
@@ -7173,23 +6805,14 @@ TextEdit::TextEdit() {
wrap_at = 0;
wrap_right_offset = 10;
set_focus_mode(FOCUS_ALL);
- syntax_highlighter = nullptr;
_update_caches();
cache.row_height = 1;
cache.line_spacing = 1;
- cache.line_number_w = 1;
- cache.breakpoint_gutter_width = 0;
- breakpoint_gutter_width = 0;
- cache.fold_gutter_width = 0;
- fold_gutter_width = 0;
- info_gutter_width = 0;
- cache.info_gutter_width = 0;
set_default_cursor_shape(CURSOR_IBEAM);
indent_size = 4;
text.set_indent_size(indent_size);
text.clear();
- text.set_color_regions(&color_regions);
h_scroll = memnew(HScrollBar);
v_scroll = memnew(VScrollBar);
@@ -7213,7 +6836,6 @@ TextEdit::TextEdit() {
selection.selecting_column = 0;
selection.selecting_text = false;
selection.active = false;
- syntax_coloring = false;
block_caret = false;
caret_blink_enabled = false;
@@ -7249,15 +6871,9 @@ TextEdit::TextEdit() {
completion_active = false;
completion_line_ofs = 0;
tooltip_obj = nullptr;
- line_numbers = false;
- line_numbers_zero_padded = false;
line_length_guidelines = false;
line_length_guideline_soft_col = 80;
line_length_guideline_hard_col = 100;
- draw_bookmark_gutter = false;
- draw_breakpoint_gutter = false;
- draw_fold_gutter = false;
- draw_info_gutter = false;
hiding_enabled = false;
next_operation_is_complex = false;
scroll_past_end_of_file_enabled = false;
@@ -7295,8 +6911,6 @@ TextEdit::TextEdit() {
set_readonly(false);
menu->connect("id_pressed", callable_mp(this, &TextEdit::menu_option));
first_draw = true;
-
- executing_line = -1;
}
TextEdit::~TextEdit() {
@@ -7304,204 +6918,6 @@ 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 != nullptr) {
- 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;
-
- bool prev_is_char = false;
- bool prev_is_number = false;
- bool in_keyword = false;
- bool in_word = false;
- bool in_function_name = false;
- bool in_member_variable = false;
- bool is_hex_notation = false;
- Color keyword_color;
- Color color;
-
- int in_region = _is_line_in_region(p_line);
- int deregion = 0;
-
- const Map<int, TextEdit::Text::ColorRegionInfo> cri_map = text.get_color_region_info(p_line);
- const String &str = text[p_line];
- Color prev_color;
- for (int j = 0; j < str.length(); j++) {
- HighlighterInfo highlighter_info;
-
- if (deregion > 0) {
- deregion--;
- if (deregion == 0) {
- in_region = -1;
- }
- }
-
- if (deregion != 0) {
- if (color != prev_color) {
- prev_color = color;
- highlighter_info.color = color;
- color_map[j] = highlighter_info;
- }
- continue;
- }
-
- color = cache.font_color;
-
- bool is_char = _is_text_char(str[j]);
- bool is_symbol = _is_symbol(str[j]);
- bool is_number = _is_number(str[j]);
-
- // 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.
- 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;
- is_char = false;
-
- if (str[j] == 'x' && str[j - 1] == '0') {
- is_hex_notation = true;
- }
- }
-
- if (!in_word && _is_char(str[j]) && !is_number) {
- in_word = true;
- }
-
- if ((in_keyword || in_word) && !is_hex_notation) {
- is_number = false;
- }
-
- if (is_symbol && str[j] != '.' && in_word) {
- in_word = false;
- }
-
- if (is_symbol && cri_map.has(j)) {
- const TextEdit::Text::ColorRegionInfo &cri = cri_map[j];
-
- if (in_region == -1) {
- if (!cri.end) {
- in_region = cri.region;
- }
- } 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();
- }
- }
- }
-
- if (!is_char) {
- in_keyword = false;
- }
-
- if (in_region == -1 && !in_keyword && is_char && !prev_is_char) {
- int to = j;
- while (to < str.length() && _is_text_char(str[to])) {
- to++;
- }
-
- uint32_t hash = String::hash(&str[j], to - j);
- StrRange range(&str[j], to - j);
-
- const Color *col = keywords.custom_getptr(range, hash);
-
- if (!col) {
- col = member_keywords.custom_getptr(range, hash);
-
- if (col) {
- for (int k = j - 1; k >= 0; k--) {
- if (str[k] == '.') {
- col = nullptr; // Member indexing not allowed.
- break;
- } else if (str[k] > 32) {
- break;
- }
- }
- }
- }
-
- if (col) {
- in_keyword = true;
- keyword_color = *col;
- }
- }
-
- if (!in_function_name && in_word && !in_keyword) {
- int k = j;
- while (k < str.length() && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
- k++;
- }
-
- // Check for space between name and bracket.
- while (k < str.length() && (str[k] == '\t' || str[k] == ' ')) {
- k++;
- }
-
- if (str[k] == '(') {
- in_function_name = true;
- }
- }
-
- if (!in_function_name && !in_member_variable && !in_keyword && !is_number && in_word) {
- int k = j;
- while (k > 0 && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
- k--;
- }
-
- if (str[k] == '.') {
- in_member_variable = true;
- }
- }
-
- if (is_symbol) {
- in_function_name = false;
- in_member_variable = false;
- }
-
- if (in_region >= 0) {
- color = color_regions[in_region].color;
- } else if (in_keyword) {
- color = keyword_color;
- } else if (in_member_variable) {
- color = cache.member_variable_color;
- } else if (in_function_name) {
- color = cache.function_color;
- } else if (is_symbol) {
- color = cache.symbol_color;
- } else if (is_number) {
- color = cache.number_color;
- }
-
- prev_is_char = is_char;
- prev_is_number = is_number;
-
- if (color != prev_color) {
- prev_color = color;
- highlighter_info.color = color;
- color_map[j] = highlighter_info;
- }
- }
-
- syntax_highlighting_cache[p_line] = color_map;
- return color_map;
-}
-
-void SyntaxHighlighter::set_text_editor(TextEdit *p_text_editor) {
- text_editor = p_text_editor;
-}
-
-TextEdit *SyntaxHighlighter::get_text_editor() {
- return text_editor;
+Dictionary TextEdit::_get_line_syntax_highlighting(int p_line) {
+ return syntax_highlighter.is_null() && !setting_text ? Dictionary() : syntax_highlighter->get_line_syntax_highlighting(p_line);
}
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index 689199b6c2..562f4768ae 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -35,121 +35,113 @@
#include "scene/gui/popup_menu.h"
#include "scene/gui/scroll_bar.h"
#include "scene/main/timer.h"
-
-class SyntaxHighlighter;
+#include "scene/resources/syntax_highlighter.h"
class TextEdit : public Control {
GDCLASS(TextEdit, Control);
public:
- struct HighlighterInfo {
- Color color;
+ enum GutterType {
+ GUTTER_TYPE_STRING,
+ GUTTER_TPYE_ICON,
+ GUTTER_TPYE_CUSTOM
};
- struct ColorRegion {
- Color color;
- String begin_key;
- String end_key;
- bool line_only;
- bool eq;
- ColorRegion(const String &p_begin_key = "", const String &p_end_key = "", const Color &p_color = Color(), bool p_line_only = false) {
- begin_key = p_begin_key;
- end_key = p_end_key;
- color = p_color;
- line_only = p_line_only || p_end_key == "";
- eq = begin_key == end_key;
- }
+private:
+ struct GutterInfo {
+ GutterType type = GutterType::GUTTER_TYPE_STRING;
+ String name = "";
+ int width = 24;
+ bool draw = true;
+ bool clickable = false;
+ bool overwritable = false;
+
+ ObjectID custom_draw_obj = ObjectID();
+ StringName custom_draw_callback;
};
+ Vector<GutterInfo> gutters;
+ int gutters_width = 0;
+ int gutter_padding = 0;
+
+ void _update_gutter_width();
class Text {
public:
- struct ColorRegionInfo {
- int region;
- bool end;
- ColorRegionInfo() {
- region = 0;
- end = false;
- }
+ struct Gutter {
+ Variant metadata;
+ bool clickable = false;
+
+ Ref<Texture2D> icon = Ref<Texture2D>();
+ String text = "";
+ Color color = Color(1, 1, 1);
};
struct Line {
- int width_cache : 24;
- bool marked : 1;
- bool breakpoint : 1;
- bool bookmark : 1;
- bool hidden : 1;
- bool safe : 1;
- bool has_info : 1;
- int wrap_amount_cache : 24;
- Map<int, ColorRegionInfo> region_info;
- Ref<Texture2D> info_icon;
- String info;
+ Vector<Gutter> gutters;
+
+ int32_t width_cache;
+ bool marked;
+ bool hidden;
+ int32_t wrap_amount_cache;
String data;
Line() {
width_cache = 0;
marked = false;
- breakpoint = false;
- bookmark = false;
hidden = false;
- safe = false;
- has_info = false;
wrap_amount_cache = 0;
}
};
private:
- const Vector<ColorRegion> *color_regions;
mutable Vector<Line> text;
Ref<Font> font;
- int indent_size;
+ int indent_size = 4;
+ int gutter_count = 0;
void _update_line_cache(int p_line) const;
public:
void set_indent_size(int p_indent_size);
void set_font(const Ref<Font> &p_font);
- void set_color_regions(const Vector<ColorRegion> *p_regions) { color_regions = p_regions; }
int get_line_width(int p_line) const;
int get_max_width(bool p_exclude_hidden = false) const;
- int get_char_width(CharType c, CharType next_c, int px) const;
+ int get_char_width(char32_t c, char32_t next_c, int px) const;
void set_line_wrap_amount(int p_line, int p_wrap_amount) const;
int get_line_wrap_amount(int p_line) const;
- const Map<int, ColorRegionInfo> &get_color_region_info(int p_line) const;
void set(int p_line, const String &p_text);
void set_marked(int p_line, bool p_marked) { text.write[p_line].marked = p_marked; }
bool is_marked(int p_line) const { return text[p_line].marked; }
- void set_bookmark(int p_line, bool p_bookmark) { text.write[p_line].bookmark = p_bookmark; }
- bool is_bookmark(int p_line) const { return text[p_line].bookmark; }
- void set_breakpoint(int p_line, bool p_breakpoint) { text.write[p_line].breakpoint = p_breakpoint; }
- bool is_breakpoint(int p_line) const { return text[p_line].breakpoint; }
void set_hidden(int p_line, bool p_hidden) { text.write[p_line].hidden = p_hidden; }
bool is_hidden(int p_line) const { return text[p_line].hidden; }
- void set_safe(int p_line, bool p_safe) { text.write[p_line].safe = p_safe; }
- bool is_safe(int p_line) const { return text[p_line].safe; }
- void set_info_icon(int p_line, Ref<Texture2D> p_icon, String p_info) {
- if (p_icon.is_null()) {
- text.write[p_line].has_info = false;
- return;
- }
- text.write[p_line].info_icon = p_icon;
- text.write[p_line].info = p_info;
- text.write[p_line].has_info = true;
- }
- bool has_info_icon(int p_line) const { return text[p_line].has_info; }
- const Ref<Texture2D> &get_info_icon(int p_line) const { return text[p_line].info_icon; }
- const String &get_info(int p_line) const { return text[p_line].info; }
void insert(int p_at, const String &p_text);
void remove(int p_at);
int size() const { return text.size(); }
void clear();
void clear_width_cache();
void clear_wrap_cache();
- void clear_info_icons();
_FORCE_INLINE_ const String &operator[](int p_line) const { return text[p_line].data; }
- Text() { indent_size = 4; }
+
+ /* Gutters. */
+ void add_gutter(int p_at);
+ void remove_gutter(int p_gutter);
+ void move_gutters(int p_from_line, int p_to_line);
+
+ void set_line_gutter_metadata(int p_line, int p_gutter, const Variant &p_metadata) { text.write[p_line].gutters.write[p_gutter].metadata = p_metadata; }
+ const Variant &get_line_gutter_metadata(int p_line, int p_gutter) const { return text[p_line].gutters[p_gutter].metadata; }
+
+ void set_line_gutter_text(int p_line, int p_gutter, const String &p_text) { text.write[p_line].gutters.write[p_gutter].text = p_text; }
+ const String &get_line_gutter_text(int p_line, int p_gutter) const { return text[p_line].gutters[p_gutter].text; }
+
+ void set_line_gutter_icon(int p_line, int p_gutter, Ref<Texture2D> p_icon) { text.write[p_line].gutters.write[p_gutter].icon = p_icon; }
+ const Ref<Texture2D> &get_line_gutter_icon(int p_line, int p_gutter) const { return text[p_line].gutters[p_gutter].icon; }
+
+ void set_line_gutter_item_color(int p_line, int p_gutter, const Color &p_color) { text.write[p_line].gutters.write[p_gutter].color = p_color; }
+ const Color &get_line_gutter_item_color(int p_line, int p_gutter) const { return text[p_line].gutters[p_gutter].color; }
+
+ void set_line_gutter_clickable(int p_line, int p_gutter, bool p_clickable) { text.write[p_line].gutters.write[p_gutter].clickable = p_clickable; }
+ bool is_line_gutter_clickable(int p_line, int p_gutter) const { return text[p_line].gutters[p_gutter].clickable; }
};
-private:
struct Cursor {
int last_fit_x;
int line, column; ///< cursor
@@ -202,67 +194,7 @@ private:
}
} selection;
- struct Cache {
- Ref<Texture2D> tab_icon;
- Ref<Texture2D> space_icon;
- Ref<Texture2D> can_fold_icon;
- Ref<Texture2D> folded_icon;
- Ref<Texture2D> folded_eol_icon;
- Ref<Texture2D> executing_icon;
- Ref<StyleBox> style_normal;
- Ref<StyleBox> style_focus;
- Ref<StyleBox> style_readonly;
- Ref<Font> font;
- Color completion_background_color;
- Color completion_selected_color;
- Color completion_existing_color;
- Color completion_font_color;
- Color caret_color;
- Color caret_background_color;
- Color line_number_color;
- Color safe_line_number_color;
- Color font_color;
- Color font_color_selected;
- Color font_color_readonly;
- Color keyword_color;
- Color number_color;
- Color function_color;
- Color member_variable_color;
- Color selection_color;
- Color mark_color;
- Color bookmark_color;
- Color breakpoint_color;
- Color executing_line_color;
- Color code_folding_color;
- Color current_line_color;
- Color line_length_guideline_color;
- Color brace_mismatch_color;
- Color word_highlighted_color;
- Color search_result_color;
- Color search_result_border_color;
- Color symbol_color;
- Color background_color;
-
- int row_height;
- int line_spacing;
- int line_number_w;
- int breakpoint_gutter_width;
- int fold_gutter_width;
- int info_gutter_width;
- int minimap_width;
- Cache() {
- row_height = 0;
- line_spacing = 0;
- line_number_w = 0;
- breakpoint_gutter_width = 0;
- fold_gutter_width = 0;
- info_gutter_width = 0;
- minimap_width = 0;
- }
- } cache;
-
- Map<int, int> color_region_cache;
- Map<int, Map<int, HighlighterInfo>> syntax_highlighting_cache;
+ Map<int, Dictionary> syntax_highlighting_cache;
struct TextOperation {
enum Type {
@@ -305,13 +237,10 @@ private:
void _do_text_op(const TextOperation &p_op, bool p_reverse);
//syntax coloring
- SyntaxHighlighter *syntax_highlighter;
- HashMap<String, Color> keywords;
- HashMap<String, Color> member_keywords;
+ Ref<SyntaxHighlighter> syntax_highlighter;
+ Set<String> keywords;
- Map<int, HighlighterInfo> _get_line_syntax_highlighting(int p_line);
-
- Vector<ColorRegion> color_regions;
+ Dictionary _get_line_syntax_highlighting(int p_line);
Set<String> completion_prefixes;
bool completion_enabled;
@@ -337,7 +266,6 @@ private:
int max_chars;
bool readonly;
- bool syntax_coloring;
bool indent_using_spaces;
int indent_size;
String space_indent;
@@ -361,19 +289,10 @@ private:
bool cursor_changed_dirty;
bool text_changed_dirty;
bool undo_enabled;
- bool line_numbers;
- bool line_numbers_zero_padded;
bool line_length_guidelines;
int line_length_guideline_soft_col;
int line_length_guideline_hard_col;
- bool draw_bookmark_gutter;
- bool draw_breakpoint_gutter;
- int breakpoint_gutter_width;
- bool draw_fold_gutter;
- int fold_gutter_width;
bool hiding_enabled;
- bool draw_info_gutter;
- int info_gutter_width;
bool draw_minimap;
int minimap_width;
Point2 minimap_char_size;
@@ -428,8 +347,7 @@ private:
bool context_menu_enabled;
bool shortcut_keys_enabled;
-
- int executing_line;
+ bool virtual_keyboard_enabled = true;
void _generate_context_menu();
@@ -485,18 +403,15 @@ private:
void _scroll_lines_down();
//void mouse_motion(const Point& p_pos, const Point& p_rel, int p_button_mask);
- Size2 get_minimum_size() const;
+ Size2 get_minimum_size() const override;
int _get_control_height() const;
- int get_row_height() const;
-
void _reset_caret_blink_timer();
void _toggle_draw_caret();
void _update_caches();
void _cursor_changed_emit();
void _text_changed_emit();
- void _line_edited_from(int p_line);
void _push_current_op();
@@ -508,7 +423,7 @@ private:
int _get_column_pos_of_word(const String &p_key, const String &p_search, uint32_t p_search_flags, int p_from_column);
- Vector<int> _search_bind(const String &p_key, uint32_t p_search_flags, int p_from_line, int p_from_column) const;
+ Dictionary _search_bind(const String &p_key, uint32_t p_search_flags, int p_from_line, int p_from_column) const;
PopupMenu *menu;
@@ -522,7 +437,45 @@ private:
int _calculate_spaces_till_next_right_indent(int column);
protected:
- virtual String get_tooltip(const Point2 &p_pos) const;
+ struct Cache {
+ Ref<Texture2D> tab_icon;
+ Ref<Texture2D> space_icon;
+ Ref<Texture2D> folded_eol_icon;
+ Ref<StyleBox> style_normal;
+ Ref<StyleBox> style_focus;
+ Ref<StyleBox> style_readonly;
+ Ref<Font> font;
+ Color completion_background_color;
+ Color completion_selected_color;
+ Color completion_existing_color;
+ Color completion_font_color;
+ Color caret_color;
+ Color caret_background_color;
+ Color font_color;
+ Color font_color_selected;
+ Color font_color_readonly;
+ Color selection_color;
+ Color mark_color;
+ Color code_folding_color;
+ Color current_line_color;
+ Color line_length_guideline_color;
+ Color brace_mismatch_color;
+ Color word_highlighted_color;
+ Color search_result_color;
+ Color search_result_border_color;
+ Color background_color;
+
+ int row_height;
+ int line_spacing;
+ int minimap_width;
+ Cache() {
+ row_height = 0;
+ line_spacing = 0;
+ minimap_width = 0;
+ }
+ } cache;
+
+ virtual String get_tooltip(const Point2 &p_pos) const override;
void _insert_text(int p_line, int p_char, const String &p_text, int *r_end_line = nullptr, int *r_end_char = nullptr);
void _remove_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column);
@@ -530,18 +483,56 @@ protected:
void _gui_input(const Ref<InputEvent> &p_gui_input);
void _notification(int p_what);
- void _consume_pair_symbol(CharType ch);
+ void _consume_pair_symbol(char32_t ch);
void _consume_backspace_for_pair_symbol(int prev_line, int prev_column);
static void _bind_methods();
public:
- SyntaxHighlighter *_get_syntax_highlighting();
- void _set_syntax_highlighting(SyntaxHighlighter *p_syntax_highlighter);
+ /* Syntax Highlighting. */
+ Ref<SyntaxHighlighter> get_syntax_highlighter();
+ void set_syntax_highlighter(Ref<SyntaxHighlighter> p_syntax_highlighter);
+
+ /* Gutters. */
+ void add_gutter(int p_at = -1);
+ void remove_gutter(int p_gutter);
+ int get_gutter_count() const;
+
+ void set_gutter_name(int p_gutter, const String &p_name);
+ String get_gutter_name(int p_gutter) const;
+
+ void set_gutter_type(int p_gutter, GutterType p_type);
+ GutterType get_gutter_type(int p_gutter) const;
- int _is_line_in_region(int p_line);
- ColorRegion _get_color_region(int p_region) const;
- Map<int, Text::ColorRegionInfo> _get_line_color_region_info(int p_line) const;
+ void set_gutter_width(int p_gutter, int p_width);
+ int get_gutter_width(int p_gutter) const;
+
+ void set_gutter_draw(int p_gutter, bool p_draw);
+ bool is_gutter_drawn(int p_gutter) const;
+
+ void set_gutter_clickable(int p_gutter, bool p_clickable);
+ bool is_gutter_clickable(int p_gutter) const;
+
+ void set_gutter_overwritable(int p_gutter, bool p_overwritable);
+ bool is_gutter_overwritable(int p_gutter) const;
+
+ void set_gutter_custom_draw(int p_gutter, Object *p_object, const StringName &p_callback);
+
+ // Line gutters.
+ void set_line_gutter_metadata(int p_line, int p_gutter, const Variant &p_metadata);
+ Variant get_line_gutter_metadata(int p_line, int p_gutter) const;
+
+ void set_line_gutter_text(int p_line, int p_gutter, const String &p_text);
+ String get_line_gutter_text(int p_line, int p_gutter) const;
+
+ void set_line_gutter_icon(int p_line, int p_gutter, Ref<Texture2D> p_icon);
+ Ref<Texture2D> get_line_gutter_icon(int p_line, int p_gutter) const;
+
+ void set_line_gutter_item_color(int p_line, int p_gutter, const Color &p_color);
+ Color get_line_gutter_item_color(int p_line, int p_gutter);
+
+ void set_line_gutter_clickable(int p_line, int p_gutter, bool p_clickable);
+ bool is_line_gutter_clickable(int p_line, int p_gutter) const;
enum MenuItems {
MENU_CUT,
@@ -561,12 +552,7 @@ public:
SEARCH_BACKWARDS = 4
};
- enum SearchResult {
- SEARCH_RESULT_COLUMN,
- SEARCH_RESULT_LINE,
- };
-
- virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const;
+ virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override;
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;
@@ -585,22 +571,6 @@ public:
void insert_at(const String &p_text, int at);
int get_line_count() const;
void set_line_as_marked(int p_line, bool p_marked);
- void set_line_as_bookmark(int p_line, bool p_bookmark);
- bool is_line_set_as_bookmark(int p_line) const;
- void get_bookmarks(List<int> *p_bookmarks) const;
- Array get_bookmarks_array() const;
- void set_line_as_breakpoint(int p_line, bool p_breakpoint);
- bool is_line_set_as_breakpoint(int p_line) const;
- void set_executing_line(int p_line);
- void clear_executing_line();
- void set_line_as_safe(int p_line, bool p_safe);
- bool is_line_set_as_safe(int p_line) const;
- void get_breakpoints(List<int> *p_breakpoints) const;
- Array get_breakpoints_array() const;
- void remove_breakpoints();
-
- void set_line_info_icon(int p_line, Ref<Texture2D> p_icon, String p_info = "");
- void clear_info_icons();
void set_line_as_hidden(int p_line, bool p_hidden);
bool is_line_hidden(int p_line) const;
@@ -620,6 +590,7 @@ public:
String get_text();
String get_line(int line) const;
void set_line(int line, String new_text);
+ int get_row_height() const;
void backspace_at_cursor();
void indent_left();
@@ -676,9 +647,6 @@ public:
void clear();
- void set_syntax_coloring(bool p_enabled);
- bool is_syntax_coloring_enabled() const;
-
void cut();
void copy();
void paste();
@@ -723,17 +691,8 @@ public:
void set_insert_mode(bool p_enabled);
bool is_insert_mode() const;
- void add_keyword_color(const String &p_keyword, const Color &p_color);
- bool has_keyword_color(String p_keyword) const;
- Color get_keyword_color(String p_keyword) const;
-
- void add_color_region(const String &p_begin_key = String(), const String &p_end_key = String(), const Color &p_color = Color(), bool p_line_only = false);
- void clear_colors();
-
- void add_member_keyword(const String &p_keyword, const Color &p_color);
- bool has_member_color(String p_member) const;
- Color get_member_color(String p_member) const;
- void clear_member_keywords();
+ void add_keyword(const String &p_keyword);
+ void clear_keywords();
double get_v_scroll() const;
void set_v_scroll(double p_scroll);
@@ -753,39 +712,13 @@ public:
void menu_option(int p_option);
- void set_show_line_numbers(bool p_show);
- bool is_show_line_numbers_enabled() const;
-
void set_highlight_current_line(bool p_enabled);
bool is_highlight_current_line_enabled() const;
- void set_line_numbers_zero_padded(bool p_zero_padded);
-
void set_show_line_length_guidelines(bool p_show);
void set_line_length_guideline_soft_column(int p_column);
void set_line_length_guideline_hard_column(int p_column);
- void set_bookmark_gutter_enabled(bool p_draw);
- bool is_bookmark_gutter_enabled() const;
-
- void set_breakpoint_gutter_enabled(bool p_draw);
- bool is_breakpoint_gutter_enabled() const;
-
- void set_breakpoint_gutter_width(int p_gutter_width);
- int get_breakpoint_gutter_width() const;
-
- void set_draw_fold_gutter(bool p_draw);
- bool is_drawing_fold_gutter() const;
-
- void set_fold_gutter_width(int p_gutter_width);
- int get_fold_gutter_width() const;
-
- void set_draw_info_gutter(bool p_draw);
- bool is_drawing_info_gutter() const;
-
- 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;
@@ -814,34 +747,21 @@ public:
void set_shortcut_keys_enabled(bool p_enabled);
bool is_shortcut_keys_enabled() const;
+ void set_virtual_keyboard_enabled(bool p_enable);
+ bool is_virtual_keyboard_enabled() const;
+
PopupMenu *get_menu() const;
String get_text_for_completion();
String get_text_for_lookup_completion();
- virtual bool is_text_field() const;
+ virtual bool is_text_field() const override;
TextEdit();
~TextEdit();
};
+VARIANT_ENUM_CAST(TextEdit::GutterType);
VARIANT_ENUM_CAST(TextEdit::MenuItems);
VARIANT_ENUM_CAST(TextEdit::SearchFlags);
-VARIANT_ENUM_CAST(TextEdit::SearchResult);
-
-class SyntaxHighlighter {
-protected:
- TextEdit *text_editor;
-
-public:
- virtual ~SyntaxHighlighter() {}
- virtual void _update_cache() = 0;
- virtual Map<int, TextEdit::HighlighterInfo> _get_line_syntax_highlighting(int p_line) = 0;
-
- virtual String get_name() const = 0;
- virtual List<String> get_supported_languages() = 0;
-
- void set_text_editor(TextEdit *p_text_editor);
- TextEdit *get_text_editor();
-};
#endif // TEXT_EDIT_H
diff --git a/scene/gui/texture_button.cpp b/scene/gui/texture_button.cpp
index 6e86f0f299..4187d77083 100644
--- a/scene/gui/texture_button.cpp
+++ b/scene/gui/texture_button.cpp
@@ -166,9 +166,11 @@ void TextureButton::_notification(int p_what) {
} break;
}
+ Point2 ofs;
+ Size2 size;
+
if (texdraw.is_valid()) {
- Point2 ofs;
- Size2 size = texdraw->get_size();
+ size = texdraw->get_size();
_texture_region = Rect2(Point2(), texdraw->get_size());
_tile = false;
if (expand) {
@@ -218,17 +220,21 @@ void TextureButton::_notification(int p_what) {
}
_position_rect = Rect2(ofs, size);
+
+ size.width *= hflip ? -1.0f : 1.0f;
+ size.height *= vflip ? -1.0f : 1.0f;
+
if (_tile) {
- draw_texture_rect(texdraw, _position_rect, _tile);
+ draw_texture_rect(texdraw, Rect2(ofs, size), _tile);
} else {
- draw_texture_rect_region(texdraw, _position_rect, _texture_region);
+ draw_texture_rect_region(texdraw, Rect2(ofs, size), _texture_region);
}
} else {
_position_rect = Rect2();
}
if (has_focus() && focused.is_valid()) {
- draw_texture_rect(focused, _position_rect, false);
+ draw_texture_rect(focused, Rect2(ofs, size), false);
};
} break;
}
@@ -243,6 +249,10 @@ void TextureButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_click_mask", "mask"), &TextureButton::set_click_mask);
ClassDB::bind_method(D_METHOD("set_expand", "p_expand"), &TextureButton::set_expand);
ClassDB::bind_method(D_METHOD("set_stretch_mode", "p_mode"), &TextureButton::set_stretch_mode);
+ ClassDB::bind_method(D_METHOD("set_flip_h", "enable"), &TextureButton::set_flip_h);
+ ClassDB::bind_method(D_METHOD("is_flipped_h"), &TextureButton::is_flipped_h);
+ ClassDB::bind_method(D_METHOD("set_flip_v", "enable"), &TextureButton::set_flip_v);
+ ClassDB::bind_method(D_METHOD("is_flipped_v"), &TextureButton::is_flipped_v);
ClassDB::bind_method(D_METHOD("get_normal_texture"), &TextureButton::get_normal_texture);
ClassDB::bind_method(D_METHOD("get_pressed_texture"), &TextureButton::get_pressed_texture);
@@ -262,6 +272,8 @@ void TextureButton::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_click_mask", PROPERTY_HINT_RESOURCE_TYPE, "BitMap"), "set_click_mask", "get_click_mask");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand", PROPERTY_HINT_RESOURCE_TYPE, "bool"), "set_expand", "get_expand");
ADD_PROPERTY(PropertyInfo(Variant::INT, "stretch_mode", PROPERTY_HINT_ENUM, "Scale,Tile,Keep,Keep Centered,Keep Aspect,Keep Aspect Centered,Keep Aspect Covered"), "set_stretch_mode", "get_stretch_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_h", PROPERTY_HINT_RESOURCE_TYPE, "bool"), "set_flip_h", "is_flipped_h");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_v", PROPERTY_HINT_RESOURCE_TYPE, "bool"), "set_flip_v", "is_flipped_v");
BIND_ENUM_CONSTANT(STRETCH_SCALE);
BIND_ENUM_CONSTANT(STRETCH_TILE);
@@ -345,9 +357,29 @@ TextureButton::StretchMode TextureButton::get_stretch_mode() const {
return stretch_mode;
}
+void TextureButton::set_flip_h(bool p_flip) {
+ hflip = p_flip;
+ update();
+}
+
+bool TextureButton::is_flipped_h() const {
+ return hflip;
+}
+
+void TextureButton::set_flip_v(bool p_flip) {
+ vflip = p_flip;
+ update();
+}
+
+bool TextureButton::is_flipped_v() const {
+ return vflip;
+}
+
TextureButton::TextureButton() {
expand = false;
stretch_mode = STRETCH_SCALE;
+ hflip = false;
+ vflip = false;
_texture_region = Rect2();
_position_rect = Rect2();
diff --git a/scene/gui/texture_button.h b/scene/gui/texture_button.h
index a1e66203d3..6f7ee65ae4 100644
--- a/scene/gui/texture_button.h
+++ b/scene/gui/texture_button.h
@@ -61,9 +61,12 @@ private:
Rect2 _position_rect;
bool _tile;
+ bool hflip;
+ bool vflip;
+
protected:
- virtual Size2 get_minimum_size() const;
- virtual bool has_point(const Point2 &p_point) const;
+ virtual Size2 get_minimum_size() const override;
+ virtual bool has_point(const Point2 &p_point) const override;
void _notification(int p_what);
static void _bind_methods();
@@ -88,6 +91,12 @@ public:
void set_stretch_mode(StretchMode p_stretch_mode);
StretchMode get_stretch_mode() const;
+ void set_flip_h(bool p_flip);
+ bool is_flipped_h() const;
+
+ void set_flip_v(bool p_flip);
+ bool is_flipped_v() const;
+
TextureButton();
};
diff --git a/scene/gui/texture_progress.h b/scene/gui/texture_progress.h
index e56454f866..5e29fca21f 100644
--- a/scene/gui/texture_progress.h
+++ b/scene/gui/texture_progress.h
@@ -93,7 +93,7 @@ public:
void set_tint_over(const Color &p_tint);
Color get_tint_over() const;
- Size2 get_minimum_size() const;
+ Size2 get_minimum_size() const override;
TextureProgress();
diff --git a/scene/gui/texture_rect.h b/scene/gui/texture_rect.h
index 727ab95776..efd3f0698a 100644
--- a/scene/gui/texture_rect.h
+++ b/scene/gui/texture_rect.h
@@ -59,7 +59,7 @@ private:
protected:
void _notification(int p_what);
- virtual Size2 get_minimum_size() const;
+ virtual Size2 get_minimum_size() const override;
static void _bind_methods();
public:
diff --git a/scene/gui/tool_button.cpp b/scene/gui/tool_button.cpp
deleted file mode 100644
index c9f87f0015..0000000000
--- a/scene/gui/tool_button.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-/*************************************************************************/
-/* tool_button.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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 "tool_button.h"
-
-ToolButton::ToolButton() {
- set_flat(true);
-}
diff --git a/scene/gui/tool_button.h b/scene/gui/tool_button.h
deleted file mode 100644
index 9848b21381..0000000000
--- a/scene/gui/tool_button.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*************************************************************************/
-/* tool_button.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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 TOOL_BUTTON_H
-#define TOOL_BUTTON_H
-
-#include "scene/gui/button.h"
-
-class ToolButton : public Button {
- GDCLASS(ToolButton, Button);
-
-public:
- ToolButton();
-};
-
-#endif // TOOL_BUTTON_H
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 45fcb448f8..070948237a 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -1208,8 +1208,9 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
if (drop_mode_flags && drop_mode_over == p_item) {
Rect2 r = cell_rect;
+ bool has_parent = p_item->get_children() != nullptr;
- if (drop_mode_section == -1 || drop_mode_section == 0) {
+ if (drop_mode_section == -1 || has_parent || drop_mode_section == 0) {
RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x, r.position.y, r.size.x, 1), cache.drop_position_color);
}
@@ -1218,7 +1219,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x + r.size.x - 1, r.position.y, 1, r.size.y), cache.drop_position_color);
}
- if (drop_mode_section == 1 || drop_mode_section == 0) {
+ if ((drop_mode_section == 1 && !has_parent) || drop_mode_section == 0) {
RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(r.position.x, r.position.y + r.size.y, r.size.x, 1), cache.drop_position_color);
}
}
@@ -1786,7 +1787,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool
case TreeItem::CELL_MODE_STRING: {
//nothing in particular
- if (select_mode == SELECT_MULTI && (get_tree()->get_event_count() == focus_in_id || !already_cursor)) {
+ if (select_mode == SELECT_MULTI && (get_viewport()->get_processed_events_count() == focus_in_id || !already_cursor)) {
bring_up_editor = false;
}
@@ -1861,7 +1862,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::range_step_decimals(p_item->cells[col].step));
- if (select_mode == SELECT_MULTI && get_tree()->get_event_count() == focus_in_id) {
+ if (select_mode == SELECT_MULTI && get_viewport()->get_processed_events_count() == focus_in_id) {
bring_up_editor = false;
}
}
@@ -1971,7 +1972,7 @@ void Tree::_text_editor_enter(String p_text) {
//popup_edited_item->edited_signal.call( popup_edited_item_col );
} break;
case TreeItem::CELL_MODE_RANGE: {
- c.val = p_text.to_double();
+ c.val = p_text.to_float();
if (c.step > 0) {
c.val = Math::stepify(c.val, c.step);
}
@@ -2364,7 +2365,6 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
if (pos.x < len) {
cache.hover_type = Cache::CLICK_TITLE;
cache.hover_index = i;
- update();
break;
}
}
@@ -2383,6 +2383,9 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
mpos.y += v_scroll->get_value();
}
+ TreeItem *old_it = cache.hover_item;
+ int old_col = cache.hover_cell;
+
int col, h, section;
TreeItem *it = _find_item_at_pos(root, mpos, col, h, section);
@@ -2397,18 +2400,26 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
}
}
- if (it != cache.hover_item) {
- cache.hover_item = it;
- update();
- }
+ cache.hover_item = it;
+ cache.hover_cell = col;
- if (it && col != cache.hover_cell) {
- cache.hover_cell = col;
- update();
+ if (it != old_it || col != old_col) {
+ if (old_it && old_col >= old_it->cells.size()) {
+ // Columns may have changed since last update().
+ update();
+ } else {
+ // Only need to update if mouse enters/exits a button
+ bool was_over_button = old_it && old_it->cells[old_col].custom_button;
+ bool is_over_button = it && it->cells[col].custom_button;
+ if (was_over_button || is_over_button) {
+ update();
+ }
+ }
}
}
}
+ // Update if mouse enters/exits columns
if (cache.hover_type != old_hover || cache.hover_index != old_index) {
update();
}
@@ -2782,7 +2793,7 @@ int Tree::_get_title_button_height() const {
void Tree::_notification(int p_what) {
if (p_what == NOTIFICATION_FOCUS_ENTER) {
- focus_in_id = get_tree()->get_event_count();
+ focus_in_id = get_viewport()->get_processed_events_count();
}
if (p_what == NOTIFICATION_MOUSE_EXIT) {
if (cache.hover_type != Cache::CLICK_NONE) {
@@ -3405,7 +3416,7 @@ void Tree::scroll_to_item(TreeItem *p_item) {
const Rect2 r = get_item_rect(p_item);
- if (r.position.y < v_scroll->get_value()) {
+ if (r.position.y <= v_scroll->get_value()) {
v_scroll->set_value(r.position.y);
} else if (r.position.y + r.size.y + 2 * cache.vseparation > v_scroll->get_value() + get_size().y) {
v_scroll->set_value(r.position.y + r.size.y + 2 * cache.vseparation - get_size().y);
@@ -3414,6 +3425,8 @@ void Tree::scroll_to_item(TreeItem *p_item) {
TreeItem *Tree::_search_item_text(TreeItem *p_at, const String &p_find, int *r_col, bool p_selectable, bool p_backwards) {
TreeItem *from = p_at;
+ TreeItem *loop = nullptr; // Safe-guard against infinite loop.
+
while (p_at) {
for (int i = 0; i < columns.size(); i++) {
if (p_at->get_text(i).findn(p_find) == 0 && (!p_selectable || p_at->is_selectable(i))) {
@@ -3433,6 +3446,12 @@ TreeItem *Tree::_search_item_text(TreeItem *p_at, const String &p_find, int *r_c
if ((p_at) == from) {
break;
}
+
+ if (!loop) {
+ loop = p_at;
+ } else if (loop == p_at) {
+ break;
+ }
}
return nullptr;
@@ -3618,6 +3637,47 @@ TreeItem *Tree::get_item_at_position(const Point2 &p_pos) const {
return nullptr;
}
+int Tree::get_button_id_at_position(const Point2 &p_pos) const {
+ if (root) {
+ Point2 pos = p_pos;
+ pos -= cache.bg->get_offset();
+ pos.y -= _get_title_button_height();
+ if (pos.y < 0) {
+ return -1;
+ }
+
+ if (h_scroll->is_visible_in_tree()) {
+ pos.x += h_scroll->get_value();
+ }
+ if (v_scroll->is_visible_in_tree()) {
+ pos.y += v_scroll->get_value();
+ }
+
+ int col, h, section;
+ TreeItem *it = _find_item_at_pos(root, pos, col, h, section);
+
+ if (it) {
+ const TreeItem::Cell &c = it->cells[col];
+ int col_width = get_column_width(col);
+
+ for (int i = 0; i < col; i++) {
+ pos.x -= get_column_width(i);
+ }
+
+ for (int j = c.buttons.size() - 1; j >= 0; j--) {
+ Ref<Texture2D> b = c.buttons[j].texture;
+ Size2 size = b->get_size() + cache.button_pressed->get_minimum_size();
+ if (pos.x > col_width - size.width) {
+ return c.buttons[j].id;
+ }
+ col_width -= size.width;
+ }
+ }
+ }
+
+ return -1;
+}
+
String Tree::get_tooltip(const Point2 &p_pos) const {
if (root) {
Point2 pos = p_pos;
@@ -3834,7 +3894,7 @@ Tree::Tree() {
add_child(popup_menu);
// popup_menu->set_as_toplevel(true);
- popup_editor = memnew(PopupPanel);
+ popup_editor = memnew(Popup);
popup_editor->set_wrap_controls(true);
add_child(popup_editor);
popup_editor_vb = memnew(VBoxContainer);
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index 46842e78a0..c0910a8fe0 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -359,11 +359,11 @@ private:
VBoxContainer *popup_editor_vb;
- PopupPanel *popup_editor;
+ Popup *popup_editor;
LineEdit *text_editor;
HSlider *value_editor;
bool updating_value_editor;
- int64_t focus_in_id;
+ uint64_t focus_in_id;
PopupMenu *popup_menu;
Vector<ColumnInfo> columns;
@@ -389,7 +389,7 @@ private:
void _gui_input(Ref<InputEvent> p_event);
void _notification(int p_what);
- Size2 get_minimum_size() const;
+ Size2 get_minimum_size() const override;
void item_edited(int p_column, TreeItem *p_item, bool p_lmb = true);
void item_changed(int p_column, TreeItem *p_item);
@@ -530,11 +530,12 @@ protected:
}
public:
- virtual String get_tooltip(const Point2 &p_pos) const;
+ virtual String get_tooltip(const Point2 &p_pos) const override;
TreeItem *get_item_at_position(const Point2 &p_pos) const;
int get_column_at_position(const Point2 &p_pos) const;
int get_drop_section_at_position(const Point2 &p_pos) const;
+ int get_button_id_at_position(const Point2 &p_pos) const;
void clear();
diff --git a/scene/gui/video_player.cpp b/scene/gui/video_player.cpp
index 881df06d8f..e118cb0d8d 100644
--- a/scene/gui/video_player.cpp
+++ b/scene/gui/video_player.cpp
@@ -201,10 +201,9 @@ bool VideoPlayer::has_expand() const {
void VideoPlayer::set_stream(const Ref<VideoStream> &p_stream) {
stop();
+
AudioServer::get_singleton()->lock();
mix_buffer.resize(AudioServer::get_singleton()->thread_get_mix_buffer_size());
- AudioServer::get_singleton()->unlock();
-
stream = p_stream;
if (stream.is_valid()) {
stream->set_audio_track(audio_track);
@@ -212,6 +211,7 @@ void VideoPlayer::set_stream(const Ref<VideoStream> &p_stream) {
} else {
playback = Ref<VideoStreamPlayback>();
}
+ AudioServer::get_singleton()->unlock();
if (!playback.is_null()) {
playback->set_loop(loops);
diff --git a/scene/gui/video_player.h b/scene/gui/video_player.h
index 551c079b3c..573aec5a2c 100644
--- a/scene/gui/video_player.h
+++ b/scene/gui/video_player.h
@@ -77,10 +77,10 @@ class VideoPlayer : public Control {
protected:
static void _bind_methods();
void _notification(int p_notification);
- void _validate_property(PropertyInfo &p_property) const;
+ void _validate_property(PropertyInfo &p_property) const override;
public:
- Size2 get_minimum_size() const;
+ Size2 get_minimum_size() const override;
void set_expand(bool p_expand);
bool has_expand() const;