summaryrefslogtreecommitdiff
path: root/scene/gui
diff options
context:
space:
mode:
Diffstat (limited to 'scene/gui')
-rw-r--r--scene/gui/base_button.cpp2
-rw-r--r--scene/gui/control.cpp33
-rw-r--r--scene/gui/control.h5
-rw-r--r--scene/gui/line_edit.cpp20
-rw-r--r--scene/gui/line_edit.h1
-rw-r--r--scene/gui/option_button.cpp2
-rw-r--r--scene/gui/popup_menu.cpp7
-rw-r--r--scene/gui/popup_menu.h1
-rw-r--r--scene/gui/scroll_container.cpp74
-rw-r--r--scene/gui/scroll_container.h6
-rw-r--r--scene/gui/tab_container.cpp161
-rw-r--r--scene/gui/tab_container.h12
-rw-r--r--scene/gui/tabs.cpp118
-rw-r--r--scene/gui/tabs.h7
-rw-r--r--scene/gui/text_edit.cpp84
-rw-r--r--scene/gui/text_edit.h7
16 files changed, 454 insertions, 86 deletions
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp
index 562dd155f9..dbfb96697d 100644
--- a/scene/gui/base_button.cpp
+++ b/scene/gui/base_button.cpp
@@ -252,7 +252,7 @@ void BaseButton::_notification(int p_what) {
status.hovering = false;
update();
}
- if (p_what == NOTIFICATION_DRAG_BEGIN) {
+ if (p_what == NOTIFICATION_DRAG_BEGIN || p_what == NOTIFICATION_SCROLL_BEGIN) {
if (status.press_attempt) {
status.press_attempt = false;
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index a5883863cd..6ca6d82807 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -1281,22 +1281,24 @@ void Control::_size_changed() {
Size2 minimum_size = get_combined_minimum_size();
- if (data.h_grow == GROW_DIRECTION_BEGIN) {
- if (minimum_size.width > new_size_cache.width) {
- new_pos_cache.x = new_pos_cache.x + new_size_cache.width - minimum_size.width;
- new_size_cache.width = minimum_size.width;
+ if (minimum_size.width > new_size_cache.width) {
+ if (data.h_grow == GROW_DIRECTION_BEGIN) {
+ new_pos_cache.x += new_size_cache.width - minimum_size.width;
+ } else if (data.h_grow == GROW_DIRECTION_BOTH) {
+ new_pos_cache.x += 0.5 * (new_size_cache.width - minimum_size.width);
}
- } else {
- new_size_cache.width = MAX(minimum_size.width, new_size_cache.width);
+
+ new_size_cache.width = minimum_size.width;
}
- if (data.v_grow == GROW_DIRECTION_BEGIN) {
- if (minimum_size.height > new_size_cache.height) {
- new_pos_cache.y = new_pos_cache.y + new_size_cache.height - minimum_size.height;
- new_size_cache.height = minimum_size.height;
+ if (minimum_size.height > new_size_cache.height) {
+ if (data.v_grow == GROW_DIRECTION_BEGIN) {
+ new_pos_cache.y += new_size_cache.height - minimum_size.height;
+ } else if (data.v_grow == GROW_DIRECTION_BOTH) {
+ new_pos_cache.y += 0.5 * (new_size_cache.height - minimum_size.height);
}
- } else {
- new_size_cache.height = MAX(minimum_size.height, new_size_cache.height);
+
+ new_size_cache.height = minimum_size.height;
}
// We use a little workaround to avoid flickering when moving the pivot with _edit_set_pivot()
@@ -2838,8 +2840,8 @@ void Control::_bind_methods() {
ADD_PROPERTYINZ(PropertyInfo(Variant::INT, "margin_bottom", PROPERTY_HINT_RANGE, "-4096,4096"), "set_margin", "get_margin", MARGIN_BOTTOM);
ADD_GROUP("Grow Direction", "grow_");
- ADD_PROPERTYNO(PropertyInfo(Variant::INT, "grow_horizontal", PROPERTY_HINT_ENUM, "Begin,End"), "set_h_grow_direction", "get_h_grow_direction");
- ADD_PROPERTYNO(PropertyInfo(Variant::INT, "grow_vertical", PROPERTY_HINT_ENUM, "Begin,End"), "set_v_grow_direction", "get_v_grow_direction");
+ ADD_PROPERTYNO(PropertyInfo(Variant::INT, "grow_horizontal", PROPERTY_HINT_ENUM, "Begin,End,Both"), "set_h_grow_direction", "get_h_grow_direction");
+ ADD_PROPERTYNO(PropertyInfo(Variant::INT, "grow_vertical", PROPERTY_HINT_ENUM, "Begin,End,Both"), "set_v_grow_direction", "get_v_grow_direction");
ADD_GROUP("Rect", "rect_");
ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "rect_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_position", "get_position");
@@ -2886,6 +2888,8 @@ void Control::_bind_methods() {
BIND_CONSTANT(NOTIFICATION_FOCUS_EXIT);
BIND_CONSTANT(NOTIFICATION_THEME_CHANGED);
BIND_CONSTANT(NOTIFICATION_MODAL_CLOSE);
+ BIND_CONSTANT(NOTIFICATION_SCROLL_BEGIN);
+ BIND_CONSTANT(NOTIFICATION_SCROLL_END);
BIND_ENUM_CONSTANT(CURSOR_ARROW);
BIND_ENUM_CONSTANT(CURSOR_IBEAM);
@@ -2939,6 +2943,7 @@ void Control::_bind_methods() {
BIND_ENUM_CONSTANT(GROW_DIRECTION_BEGIN);
BIND_ENUM_CONSTANT(GROW_DIRECTION_END);
+ BIND_ENUM_CONSTANT(GROW_DIRECTION_BOTH);
BIND_ENUM_CONSTANT(ANCHOR_BEGIN);
BIND_ENUM_CONSTANT(ANCHOR_END);
diff --git a/scene/gui/control.h b/scene/gui/control.h
index 51325f27b5..a215490295 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -60,7 +60,8 @@ public:
enum GrowDirection {
GROW_DIRECTION_BEGIN,
- GROW_DIRECTION_END
+ GROW_DIRECTION_END,
+ GROW_DIRECTION_BOTH
};
enum FocusMode {
@@ -271,6 +272,8 @@ public:
NOTIFICATION_FOCUS_EXIT = 44,
NOTIFICATION_THEME_CHANGED = 45,
NOTIFICATION_MODAL_CLOSE = 46,
+ NOTIFICATION_SCROLL_BEGIN = 47,
+ NOTIFICATION_SCROLL_END = 48,
};
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 5c0e8fefc7..09c6a3b32c 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -602,6 +602,7 @@ void LineEdit::_notification(int p_what) {
}
int x_ofs = 0;
+ int cached_text_width = text.empty() ? cached_placeholder_width : cached_width;
switch (align) {
@@ -615,15 +616,15 @@ void LineEdit::_notification(int p_what) {
if (window_pos != 0)
x_ofs = style->get_offset().x;
else
- x_ofs = int(size.width - (cached_width)) / 2;
+ x_ofs = MAX(style->get_margin(MARGIN_LEFT), int(size.width - (cached_text_width)) / 2);
} break;
case ALIGN_RIGHT: {
- x_ofs = int(size.width - style->get_offset().x - (cached_width));
+ x_ofs = MAX(style->get_margin(MARGIN_LEFT), int(size.width - style->get_margin(MARGIN_RIGHT) - (cached_text_width)));
} break;
}
- int ofs_max = width - style->get_minimum_size().width;
+ int ofs_max = width - style->get_margin(MARGIN_RIGHT);
int char_ofs = window_pos;
int y_area = height - style->get_minimum_size().height;
@@ -881,7 +882,7 @@ void LineEdit::set_cursor_at_pixel_pos(int p_x) {
} break;
case ALIGN_RIGHT: {
- pixel_ofs = int(size.width - style->get_offset().x - (cached_width));
+ pixel_ofs = int(size.width - style->get_margin(MARGIN_RIGHT) - (cached_width));
} break;
}
@@ -1014,6 +1015,15 @@ String LineEdit::get_text() const {
void LineEdit::set_placeholder(String p_text) {
placeholder = tr(p_text);
+ if ((max_length <= 0) || (placeholder.length() <= max_length)) {
+ Ref<Font> font = get_font("font");
+ cached_placeholder_width = 0;
+ if (font != NULL) {
+ for (int i = 0; i < placeholder.length(); i++) {
+ cached_placeholder_width += font->get_char_size(placeholder[i]).width;
+ }
+ }
+ }
update();
}
@@ -1127,6 +1137,7 @@ void LineEdit::clear_internal() {
_clear_undo_stack();
cached_width = 0;
+ cached_placeholder_width = 0;
cursor_pos = 0;
window_pos = 0;
undo_text = "";
@@ -1468,6 +1479,7 @@ LineEdit::LineEdit() {
_create_undo_state();
align = ALIGN_LEFT;
cached_width = 0;
+ cached_placeholder_width = 0;
cursor_pos = 0;
window_pos = 0;
window_has_focus = true;
diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h
index e3ad3b17f1..c60ea36cc1 100644
--- a/scene/gui/line_edit.h
+++ b/scene/gui/line_edit.h
@@ -84,6 +84,7 @@ private:
int max_length; // 0 for no maximum
int cached_width;
+ int cached_placeholder_width;
struct Selection {
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index aaad10f579..a9402d6404 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -112,7 +112,7 @@ void OptionButton::pressed() {
void OptionButton::add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID) {
- popup->add_icon_check_item(p_icon, p_label, p_ID);
+ popup->add_icon_radio_check_item(p_icon, p_label, p_ID);
if (popup->get_item_count() == 1)
select(0);
}
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index 9ff3bd6e81..fd2466407e 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -588,6 +588,13 @@ void PopupMenu::add_radio_check_item(const String &p_label, int p_ID, uint32_t p
update();
}
+void PopupMenu::add_icon_radio_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID, uint32_t p_accel) {
+
+ add_icon_check_item(p_icon, p_label, p_ID, p_accel);
+ items[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
+ update();
+}
+
void PopupMenu::add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) {
ERR_FAIL_COND(p_shortcut.is_null());
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index c7851969d0..fde91bd845 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -122,6 +122,7 @@ public:
void add_icon_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID = -1, uint32_t p_accel = 0);
void add_check_item(const String &p_label, int p_ID = -1, uint32_t p_accel = 0);
void add_radio_check_item(const String &p_label, int p_ID = -1, uint32_t p_accel = 0);
+ void add_icon_radio_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID = -1, uint32_t p_accel = 0);
void add_submenu_item(const String &p_label, const String &p_submenu, int p_ID = -1);
void add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false);
diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp
index 33b3d46486..217df275b9 100644
--- a/scene/gui/scroll_container.cpp
+++ b/scene/gui/scroll_container.cpp
@@ -75,6 +75,12 @@ void ScrollContainer::_cancel_drag() {
drag_accum = Vector2();
last_drag_accum = Vector2();
drag_from = Vector2();
+
+ if (beyond_deadzone) {
+ emit_signal("scroll_ended");
+ propagate_notification(NOTIFICATION_SCROLL_END);
+ beyond_deadzone = false;
+ }
}
void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
@@ -122,13 +128,7 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
if (mb->is_pressed()) {
if (drag_touching) {
- set_physics_process(false);
- drag_touching_deaccel = false;
- drag_touching = false;
- drag_speed = Vector2();
- drag_accum = Vector2();
- last_drag_accum = Vector2();
- drag_from = Vector2();
+ _cancel_drag();
}
if (true) {
@@ -138,6 +138,7 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
drag_from = Vector2(h_scroll->get_value(), v_scroll->get_value());
drag_touching = OS::get_singleton()->has_touchscreen_ui_hint();
drag_touching_deaccel = false;
+ beyond_deadzone = false;
time_since_motion = 0;
if (drag_touching) {
set_physics_process(true);
@@ -149,9 +150,7 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
if (drag_touching) {
if (drag_speed == Vector2()) {
- drag_touching_deaccel = false;
- drag_touching = false;
- set_physics_process(false);
+ _cancel_drag();
} else {
drag_touching_deaccel = true;
@@ -168,17 +167,27 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
Vector2 motion = Vector2(mm->get_relative().x, mm->get_relative().y);
drag_accum -= motion;
- Vector2 diff = drag_from + drag_accum;
-
- if (scroll_h)
- h_scroll->set_value(diff.x);
- else
- drag_accum.x = 0;
- if (scroll_v)
- v_scroll->set_value(diff.y);
- else
- drag_accum.y = 0;
- time_since_motion = 0;
+
+ if (beyond_deadzone || scroll_h && Math::abs(drag_accum.x) > deadzone || scroll_v && Math::abs(drag_accum.y) > deadzone) {
+ if (!beyond_deadzone) {
+ propagate_notification(NOTIFICATION_SCROLL_BEGIN);
+ emit_signal("scroll_started");
+
+ beyond_deadzone = true;
+ // resetting drag_accum here ensures smooth scrolling after reaching deadzone
+ drag_accum = -motion;
+ }
+ Vector2 diff = drag_from + drag_accum;
+ if (scroll_h)
+ h_scroll->set_value(diff.x);
+ else
+ drag_accum.x = 0;
+ if (scroll_v)
+ v_scroll->set_value(diff.y);
+ else
+ drag_accum.y = 0;
+ time_since_motion = 0;
+ }
}
}
@@ -323,9 +332,7 @@ void ScrollContainer::_notification(int p_what) {
drag_speed = Vector2(sgn_x * val_x, sgn_y * val_y);
if (turnoff_h && turnoff_v) {
- set_physics_process(false);
- drag_touching = false;
- drag_touching_deaccel = false;
+ _cancel_drag();
}
} else {
@@ -430,6 +437,14 @@ void ScrollContainer::set_h_scroll(int p_pos) {
_cancel_drag();
}
+int ScrollContainer::get_deadzone() const {
+ return deadzone;
+}
+
+void ScrollContainer::set_deadzone(int p_deadzone) {
+ deadzone = p_deadzone;
+}
+
String ScrollContainer::get_configuration_warning() const {
int found = 0;
@@ -466,12 +481,20 @@ void ScrollContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_h_scroll"), &ScrollContainer::get_h_scroll);
ClassDB::bind_method(D_METHOD("set_v_scroll", "value"), &ScrollContainer::set_v_scroll);
ClassDB::bind_method(D_METHOD("get_v_scroll"), &ScrollContainer::get_v_scroll);
+ ClassDB::bind_method(D_METHOD("set_deadzone", "deadzone"), &ScrollContainer::set_deadzone);
+ ClassDB::bind_method(D_METHOD("get_deadzone"), &ScrollContainer::get_deadzone);
+
+ ADD_SIGNAL(MethodInfo("scroll_started"));
+ ADD_SIGNAL(MethodInfo("scroll_ended"));
ADD_GROUP("Scroll", "scroll_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_horizontal_enabled"), "set_enable_h_scroll", "is_h_scroll_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_horizontal"), "set_h_scroll", "get_h_scroll");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_vertical_enabled"), "set_enable_v_scroll", "is_v_scroll_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_vertical"), "set_v_scroll", "get_v_scroll");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_deadzone"), "set_deadzone", "get_deadzone");
+
+ GLOBAL_DEF("gui/common/default_scroll_deadzone", 0);
};
ScrollContainer::ScrollContainer() {
@@ -490,8 +513,11 @@ ScrollContainer::ScrollContainer() {
drag_speed = Vector2();
drag_touching = false;
drag_touching_deaccel = false;
+ beyond_deadzone = false;
scroll_h = true;
scroll_v = true;
+ deadzone = GLOBAL_GET("gui/common/default_scroll_deadzone");
+
set_clip_contents(true);
};
diff --git a/scene/gui/scroll_container.h b/scene/gui/scroll_container.h
index 6e3387918b..3fe1ed447a 100644
--- a/scene/gui/scroll_container.h
+++ b/scene/gui/scroll_container.h
@@ -56,10 +56,13 @@ class ScrollContainer : public Container {
bool drag_touching;
bool drag_touching_deaccel;
bool click_handled;
+ bool beyond_deadzone;
bool scroll_h;
bool scroll_v;
+ int deadzone;
+
void _cancel_drag();
protected:
@@ -86,6 +89,9 @@ public:
void set_enable_v_scroll(bool p_enable);
bool is_v_scroll_enabled() const;
+ int get_deadzone() const;
+ void set_deadzone(int p_deadzone);
+
virtual bool clips_input() const;
virtual String get_configuration_warning() const;
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index 6e85ce5eb4..0363dd44c2 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -31,6 +31,9 @@
#include "tab_container.h"
#include "message_queue.h"
+#include "scene/gui/box_container.h"
+#include "scene/gui/label.h"
+#include "scene/gui/texture_rect.h"
int TabContainer::_get_top_margin() const {
@@ -492,6 +495,141 @@ void TabContainer::_update_current_tab() {
set_current_tab(current);
}
+Variant TabContainer::get_drag_data(const Point2 &p_point) {
+
+ if (!drag_to_rearrange_enabled)
+ return Variant();
+
+ int tab_over = get_tab_idx_at_point(p_point);
+
+ if (tab_over < 0)
+ return Variant();
+
+ HBoxContainer *drag_preview = memnew(HBoxContainer);
+
+ Ref<Texture> icon = get_tab_icon(tab_over);
+ if (!icon.is_null()) {
+ TextureRect *tf = memnew(TextureRect);
+ tf->set_texture(icon);
+ drag_preview->add_child(tf);
+ }
+ Label *label = memnew(Label(get_tab_title(tab_over)));
+ drag_preview->add_child(label);
+ set_drag_preview(drag_preview);
+
+ Dictionary drag_data;
+ drag_data["type"] = "tabc_element";
+ drag_data["tabc_element"] = tab_over;
+ drag_data["from_path"] = get_path();
+ return drag_data;
+}
+
+bool TabContainer::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
+
+ if (!drag_to_rearrange_enabled)
+ return false;
+
+ Dictionary d = p_data;
+ if (!d.has("type"))
+ return false;
+
+ if (String(d["type"]) == "tabc_element") {
+
+ NodePath from_path = d["from_path"];
+ NodePath to_path = get_path();
+ if (from_path == to_path) {
+ return true;
+ } else if (get_tabs_rearrange_group() != -1) {
+ // drag and drop between other TabContainers
+ Node *from_node = get_node(from_path);
+ TabContainer *from_tabc = Object::cast_to<TabContainer>(from_node);
+ if (from_tabc && from_tabc->get_tabs_rearrange_group() == get_tabs_rearrange_group()) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void TabContainer::drop_data(const Point2 &p_point, const Variant &p_data) {
+
+ if (!drag_to_rearrange_enabled)
+ return;
+
+ int hover_now = get_tab_idx_at_point(p_point);
+
+ Dictionary d = p_data;
+ if (!d.has("type"))
+ return;
+
+ if (String(d["type"]) == "tabc_element") {
+
+ int tab_from_id = d["tabc_element"];
+ NodePath from_path = d["from_path"];
+ NodePath to_path = get_path();
+ if (from_path == to_path) {
+ if (hover_now < 0)
+ hover_now = get_tab_count() - 1;
+ move_child(get_tab_control(tab_from_id), hover_now);
+ set_current_tab(hover_now);
+ } else if (get_tabs_rearrange_group() != -1) {
+ // drag and drop between TabContainers
+ Node *from_node = get_node(from_path);
+ TabContainer *from_tabc = Object::cast_to<TabContainer>(from_node);
+ if (from_tabc && from_tabc->get_tabs_rearrange_group() == get_tabs_rearrange_group()) {
+ Control *moving_tabc = from_tabc->get_tab_control(tab_from_id);
+ from_tabc->remove_child(moving_tabc);
+ add_child(moving_tabc);
+ if (hover_now < 0)
+ hover_now = get_tab_count() - 1;
+ move_child(moving_tabc, hover_now);
+ set_current_tab(hover_now);
+ emit_signal("tab_changed", hover_now);
+ }
+ }
+ }
+ update();
+}
+
+int TabContainer::get_tab_idx_at_point(const Point2 &p_point) const {
+
+ if (get_tab_count() == 0)
+ return -1;
+
+ // must be on tabs in the tab header area.
+ if (p_point.x < tabs_ofs_cache || p_point.y > _get_top_margin())
+ return -1;
+
+ Size2 size = get_size();
+ int right_ofs = 0;
+
+ if (popup) {
+ Ref<Texture> menu = get_icon("menu");
+ right_ofs += menu->get_width();
+ }
+ if (buttons_visible_cache) {
+ Ref<Texture> increment = get_icon("increment");
+ Ref<Texture> decrement = get_icon("decrement");
+ right_ofs += increment->get_width() + decrement->get_width();
+ }
+ if (p_point.x > size.width - right_ofs) {
+ return -1;
+ }
+
+ // get the tab at the point
+ Vector<Control *> tabs = _get_tabs();
+ int px = p_point.x;
+ px -= tabs_ofs_cache;
+ for (int i = first_tab_cache; i <= last_tab_cache; i++) {
+ int tab_width = _get_tab_width(i);
+ if (px < tab_width) {
+ return i;
+ }
+ px -= tab_width;
+ }
+ return -1;
+}
+
void TabContainer::set_tab_align(TabAlign p_align) {
ERR_FAIL_INDEX(p_align, 3);
@@ -500,6 +638,7 @@ void TabContainer::set_tab_align(TabAlign p_align) {
_change_notify("tab_align");
}
+
TabContainer::TabAlign TabContainer::get_tab_align() const {
return align;
@@ -643,6 +782,21 @@ Popup *TabContainer::get_popup() const {
return popup;
}
+void TabContainer::set_drag_to_rearrange_enabled(bool p_enabled) {
+ drag_to_rearrange_enabled = p_enabled;
+}
+
+bool TabContainer::get_drag_to_rearrange_enabled() const {
+ return drag_to_rearrange_enabled;
+}
+void TabContainer::set_tabs_rearrange_group(int p_group_id) {
+ tabs_rearrange_group = p_group_id;
+}
+
+int TabContainer::get_tabs_rearrange_group() const {
+ return tabs_rearrange_group;
+}
+
void TabContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("_gui_input"), &TabContainer::_gui_input);
@@ -664,6 +818,10 @@ void TabContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tab_disabled", "tab_idx"), &TabContainer::get_tab_disabled);
ClassDB::bind_method(D_METHOD("set_popup", "popup"), &TabContainer::set_popup);
ClassDB::bind_method(D_METHOD("get_popup"), &TabContainer::get_popup);
+ ClassDB::bind_method(D_METHOD("set_drag_to_rearrange_enabled", "enabled"), &TabContainer::set_drag_to_rearrange_enabled);
+ ClassDB::bind_method(D_METHOD("get_drag_to_rearrange_enabled"), &TabContainer::get_drag_to_rearrange_enabled);
+ ClassDB::bind_method(D_METHOD("set_tabs_rearrange_group", "group_id"), &TabContainer::set_tabs_rearrange_group);
+ ClassDB::bind_method(D_METHOD("get_tabs_rearrange_group"), &TabContainer::get_tabs_rearrange_group);
ClassDB::bind_method(D_METHOD("_child_renamed_callback"), &TabContainer::_child_renamed_callback);
ClassDB::bind_method(D_METHOD("_on_theme_changed"), &TabContainer::_on_theme_changed);
@@ -676,6 +834,7 @@ void TabContainer::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_align", "get_tab_align");
ADD_PROPERTY(PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE, "-1,4096,1", PROPERTY_USAGE_EDITOR), "set_current_tab", "get_current_tab");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "tabs_visible"), "set_tabs_visible", "are_tabs_visible");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_to_rearrange_enabled"), "set_drag_to_rearrange_enabled", "get_drag_to_rearrange_enabled");
BIND_ENUM_CONSTANT(ALIGN_LEFT);
BIND_ENUM_CONSTANT(ALIGN_CENTER);
@@ -694,4 +853,6 @@ TabContainer::TabContainer() {
align = ALIGN_CENTER;
tabs_visible = true;
popup = NULL;
+ drag_to_rearrange_enabled = false;
+ tabs_rearrange_group = -1;
}
diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h
index 4bc6e00145..1afe5f7541 100644
--- a/scene/gui/tab_container.h
+++ b/scene/gui/tab_container.h
@@ -58,6 +58,8 @@ private:
Control *_get_tab(int p_idx) const;
int _get_top_margin() const;
Popup *popup;
+ bool drag_to_rearrange_enabled;
+ int tabs_rearrange_group;
Vector<Control *> _get_tabs() const;
int _get_tab_width(int p_index) const;
@@ -71,6 +73,11 @@ protected:
virtual void add_child_notify(Node *p_child);
virtual void remove_child_notify(Node *p_child);
+ 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);
+ int get_tab_idx_at_point(const Point2 &p_point) const;
+
static void _bind_methods();
public:
@@ -104,6 +111,11 @@ public:
void set_popup(Node *p_popup);
Popup *get_popup() const;
+ void set_drag_to_rearrange_enabled(bool p_enabled);
+ bool get_drag_to_rearrange_enabled() const;
+ void set_tabs_rearrange_group(int p_group_id);
+ int get_tabs_rearrange_group() const;
+
TabContainer();
};
diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp
index dee32aef7a..b114264de1 100644
--- a/scene/gui/tabs.cpp
+++ b/scene/gui/tabs.cpp
@@ -31,6 +31,9 @@
#include "tabs.h"
#include "message_queue.h"
+#include "scene/gui/box_container.h"
+#include "scene/gui/label.h"
+#include "scene/gui/texture_rect.h"
Size2 Tabs::get_minimum_size() const {
@@ -624,20 +627,105 @@ void Tabs::remove_tab(int p_idx) {
Variant Tabs::get_drag_data(const Point2 &p_point) {
- return get_tab_idx_at_point(p_point);
+ if (!drag_to_rearrange_enabled)
+ return Variant();
+
+ int tab_over = get_tab_idx_at_point(p_point);
+
+ if (tab_over < 0)
+ return Variant();
+
+ HBoxContainer *drag_preview = memnew(HBoxContainer);
+
+ if (!tabs[tab_over].icon.is_null()) {
+ TextureRect *tf = memnew(TextureRect);
+ tf->set_texture(tabs[tab_over].icon);
+ drag_preview->add_child(tf);
+ }
+ Label *label = memnew(Label(tabs[tab_over].text));
+ drag_preview->add_child(label);
+ if (!tabs[tab_over].right_button.is_null()) {
+ TextureRect *tf = memnew(TextureRect);
+ tf->set_texture(tabs[tab_over].right_button);
+ drag_preview->add_child(tf);
+ }
+ set_drag_preview(drag_preview);
+
+ Dictionary drag_data;
+ drag_data["type"] = "tab_element";
+ drag_data["tab_element"] = tab_over;
+ drag_data["from_path"] = get_path();
+ return drag_data;
}
bool Tabs::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
- return get_tab_idx_at_point(p_point) > -1;
+ if (!drag_to_rearrange_enabled)
+ return false;
+
+ Dictionary d = p_data;
+ if (!d.has("type"))
+ return false;
+
+ if (String(d["type"]) == "tab_element") {
+
+ NodePath from_path = d["from_path"];
+ NodePath to_path = get_path();
+ if (from_path == to_path) {
+ return true;
+ } else if (get_tabs_rearrange_group() != -1) {
+ // drag and drop between other Tabs
+ Node *from_node = get_node(from_path);
+ Tabs *from_tabs = Object::cast_to<Tabs>(from_node);
+ if (from_tabs && from_tabs->get_tabs_rearrange_group() == get_tabs_rearrange_group()) {
+ return true;
+ }
+ }
+ }
+ return false;
}
void Tabs::drop_data(const Point2 &p_point, const Variant &p_data) {
+ if (!drag_to_rearrange_enabled)
+ return;
+
int hover_now = get_tab_idx_at_point(p_point);
- ERR_FAIL_INDEX(hover_now, tabs.size());
- emit_signal("reposition_active_tab_request", hover_now);
+ Dictionary d = p_data;
+ if (!d.has("type"))
+ return;
+
+ if (String(d["type"]) == "tab_element") {
+
+ int tab_from_id = d["tab_element"];
+ NodePath from_path = d["from_path"];
+ NodePath to_path = get_path();
+ if (from_path == to_path) {
+ if (hover_now < 0)
+ hover_now = get_tab_count() - 1;
+ move_tab(tab_from_id, hover_now);
+ emit_signal("reposition_active_tab_request", hover_now);
+ set_current_tab(hover_now);
+ } else if (get_tabs_rearrange_group() != -1) {
+ // drag and drop between Tabs
+ Node *from_node = get_node(from_path);
+ Tabs *from_tabs = Object::cast_to<Tabs>(from_node);
+ if (from_tabs && from_tabs->get_tabs_rearrange_group() == get_tabs_rearrange_group()) {
+ if (tab_from_id >= from_tabs->get_tab_count())
+ return;
+ Tab moving_tab = from_tabs->tabs[tab_from_id];
+ if (hover_now < 0)
+ hover_now = get_tab_count();
+ tabs.insert(hover_now, moving_tab);
+ from_tabs->remove_tab(tab_from_id);
+ set_current_tab(hover_now);
+ emit_signal("tab_changed", hover_now);
+ _update_cache();
+ }
+ }
+ }
+ update();
}
int Tabs::get_tab_idx_at_point(const Point2 &p_point) const {
@@ -817,6 +905,21 @@ bool Tabs::get_scrolling_enabled() const {
return scrolling_enabled;
}
+void Tabs::set_drag_to_rearrange_enabled(bool p_enabled) {
+ drag_to_rearrange_enabled = p_enabled;
+}
+
+bool Tabs::get_drag_to_rearrange_enabled() const {
+ return drag_to_rearrange_enabled;
+}
+void Tabs::set_tabs_rearrange_group(int p_group_id) {
+ tabs_rearrange_group = p_group_id;
+}
+
+int Tabs::get_tabs_rearrange_group() const {
+ return tabs_rearrange_group;
+}
+
void Tabs::_bind_methods() {
ClassDB::bind_method(D_METHOD("_gui_input"), &Tabs::_gui_input);
@@ -842,6 +945,10 @@ void Tabs::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tab_close_display_policy"), &Tabs::get_tab_close_display_policy);
ClassDB::bind_method(D_METHOD("set_scrolling_enabled", "enabled"), &Tabs::set_scrolling_enabled);
ClassDB::bind_method(D_METHOD("get_scrolling_enabled"), &Tabs::get_scrolling_enabled);
+ ClassDB::bind_method(D_METHOD("set_drag_to_rearrange_enabled", "enabled"), &Tabs::set_drag_to_rearrange_enabled);
+ ClassDB::bind_method(D_METHOD("get_drag_to_rearrange_enabled"), &Tabs::get_drag_to_rearrange_enabled);
+ ClassDB::bind_method(D_METHOD("set_tabs_rearrange_group", "group_id"), &Tabs::set_tabs_rearrange_group);
+ ClassDB::bind_method(D_METHOD("get_tabs_rearrange_group"), &Tabs::get_tabs_rearrange_group);
ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("right_button_pressed", PropertyInfo(Variant::INT, "tab")));
@@ -854,6 +961,7 @@ void Tabs::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_align", "get_tab_align");
ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "tab_close_display_policy", PROPERTY_HINT_ENUM, "Show Never,Show Active Only,Show Always"), "set_tab_close_display_policy", "get_tab_close_display_policy");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scrolling_enabled"), "set_scrolling_enabled", "get_scrolling_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_to_rearrange_enabled"), "set_drag_to_rearrange_enabled", "get_drag_to_rearrange_enabled");
BIND_ENUM_CONSTANT(ALIGN_LEFT);
BIND_ENUM_CONSTANT(ALIGN_CENTER);
@@ -884,4 +992,6 @@ Tabs::Tabs() {
scrolling_enabled = true;
buttons_visible = false;
hover = -1;
+ drag_to_rearrange_enabled = false;
+ tabs_rearrange_group = -1;
}
diff --git a/scene/gui/tabs.h b/scene/gui/tabs.h
index 246b3cba67..3b38e7f2cb 100644
--- a/scene/gui/tabs.h
+++ b/scene/gui/tabs.h
@@ -90,6 +90,8 @@ private:
int hover; // hovered tab
int min_width;
bool scrolling_enabled;
+ bool drag_to_rearrange_enabled;
+ int tabs_rearrange_group;
int get_tab_width(int p_idx) const;
void _ensure_no_over_offset();
@@ -143,6 +145,11 @@ public:
void set_scrolling_enabled(bool p_enabled);
bool get_scrolling_enabled() const;
+ void set_drag_to_rearrange_enabled(bool p_enabled);
+ bool get_drag_to_rearrange_enabled() const;
+ void set_tabs_rearrange_group(int p_group_id);
+ int get_tabs_rearrange_group() const;
+
void ensure_tab_visible(int p_idx);
void set_min_width(int p_width);
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index e214a020d5..cc6a677ec8 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -145,7 +145,6 @@ void TextEdit::Text::_update_line_cache(int p_line) const {
text[p_line].region_info.clear();
- int ending_color_region = -1;
for (int i = 0; i < len; i++) {
if (!_is_symbol(str[i]))
@@ -186,11 +185,6 @@ void TextEdit::Text::_update_line_cache(int p_line) const {
text[p_line].region_info[i] = cri;
i += lr - 1;
- if (ending_color_region == -1 && !cr.line_only) {
- ending_color_region = j;
- } else if (ending_color_region == j) {
- ending_color_region = -1;
- }
break;
}
@@ -219,15 +213,10 @@ void TextEdit::Text::_update_line_cache(int p_line) const {
text[p_line].region_info[i] = cri;
i += lr - 1;
- if (ending_color_region == j) {
- ending_color_region = -1;
- }
-
break;
}
}
}
- text[p_line].ending_color_region = ending_color_region;
}
const Map<int, TextEdit::Text::ColorRegionInfo> &TextEdit::Text::get_color_region_info(int p_line) const {
@@ -569,7 +558,6 @@ void TextEdit::_notification(int p_what) {
}
} break;
case NOTIFICATION_DRAW: {
-
if ((!has_focus() && !menu->has_focus()) || !window_has_focus) {
draw_caret = false;
}
@@ -3196,6 +3184,7 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i
MessageQueue::get_singleton()->push_call(this, "_text_changed_emit");
text_changed_dirty = true;
}
+ _line_edited_from(p_line);
}
String TextEdit::_base_get_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column) const {
@@ -3246,6 +3235,7 @@ void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_li
MessageQueue::get_singleton()->push_call(this, "_text_changed_emit");
text_changed_dirty = true;
}
+ _line_edited_from(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) {
@@ -3368,6 +3358,13 @@ 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);
+ }
+}
+
int TextEdit::get_char_count() {
int totalsize = 0;
@@ -4009,15 +4006,49 @@ void TextEdit::_set_syntax_highlighting(SyntaxHighlighter *p_syntax_highlighter)
update();
}
-int TextEdit::_get_line_ending_color_region(int p_line) const {
- if (p_line < 0 || p_line > text.size() - 1) {
- return -1;
+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];
+ }
+
+ // if not find the closest line we have
+ int previous_line = p_line - 1;
+ for (previous_line; previous_line > -1; previous_line--) {
+ if (color_region_cache.has(p_line)) {
+ break;
+ }
+ }
+
+ // calculate up to line we need and update the cache along the way.
+ int in_region = color_region_cache[previous_line];
+ 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;
+ }
+ }
+ }
+
+ if (in_region >= 0 && _get_color_region(in_region).line_only) {
+ in_region = -1;
+ }
+
+ color_region_cache[i + 1] = in_region;
}
- return text.get_line_ending_color_region(p_line);
+ return in_region;
}
TextEdit::ColorRegion TextEdit::_get_color_region(int p_region) const {
- if (p_region < 0 || p_region > color_regions.size()) {
+ if (p_region < 0 || p_region >= color_regions.size()) {
return ColorRegion();
}
return color_regions[p_region];
@@ -4034,6 +4065,7 @@ void TextEdit::clear_colors() {
keywords.clear();
color_regions.clear();
+ color_region_cache.clear();
text.clear_caches();
}
@@ -5777,24 +5809,8 @@ Map<int, TextEdit::HighlighterInfo> TextEdit::_get_line_syntax_highlighting(int
Color keyword_color;
Color color;
- int in_region = -1;
+ int in_region = _is_line_in_region(p_line);
int deregion = 0;
- for (int i = 0; i < p_line; i++) {
- int ending_color_region = text.get_line_ending_color_region(i);
- if (in_region == -1) {
- in_region = ending_color_region;
- } else if (in_region == ending_color_region) {
- in_region = -1;
- } else {
- const Map<int, TextEdit::Text::ColorRegionInfo> &cri_map = text.get_color_region_info(i);
- for (const Map<int, TextEdit::Text::ColorRegionInfo>::Element *E = cri_map.front(); E; E = E->next()) {
- const TextEdit::Text::ColorRegionInfo &cri = E->get();
- if (cri.region == in_region) {
- in_region = -1;
- }
- }
- }
- }
const Map<int, TextEdit::Text::ColorRegionInfo> cri_map = text.get_color_region_info(p_line);
const String &str = text[p_line];
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index 2360ce79db..30e70bfd0b 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -76,7 +76,6 @@ public:
bool marked : 1;
bool breakpoint : 1;
bool hidden : 1;
- int ending_color_region;
Map<int, ColorRegionInfo> region_info;
String data;
};
@@ -103,7 +102,6 @@ public:
bool is_breakpoint(int p_line) const { return text[p_line].breakpoint; }
void set_hidden(int p_line, bool p_hidden) { text[p_line].hidden = p_hidden; }
bool is_hidden(int p_line) const { return text[p_line].hidden; }
- int get_line_ending_color_region(int p_line) const { return text[p_line].ending_color_region; }
void insert(int p_at, const String &p_text);
void remove(int p_at);
int size() const { return text.size(); }
@@ -189,6 +187,8 @@ private:
Size2 size;
} cache;
+ Map<int, int> color_region_cache;
+
struct TextOperation {
enum Type {
@@ -368,6 +368,7 @@ private:
void _update_caches();
void _cursor_changed_emit();
void _text_changed_emit();
+ void _line_edited_from(int p_line);
void _push_current_op();
@@ -407,7 +408,7 @@ public:
SyntaxHighlighter *_get_syntax_highlighting();
void _set_syntax_highlighting(SyntaxHighlighter *p_syntax_highlighter);
- int _get_line_ending_color_region(int p_line) 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;