diff options
Diffstat (limited to 'scene/gui')
-rw-r--r-- | scene/gui/base_button.cpp | 2 | ||||
-rw-r--r-- | scene/gui/control.cpp | 33 | ||||
-rw-r--r-- | scene/gui/control.h | 5 | ||||
-rw-r--r-- | scene/gui/line_edit.cpp | 20 | ||||
-rw-r--r-- | scene/gui/line_edit.h | 1 | ||||
-rw-r--r-- | scene/gui/option_button.cpp | 2 | ||||
-rw-r--r-- | scene/gui/popup_menu.cpp | 7 | ||||
-rw-r--r-- | scene/gui/popup_menu.h | 1 | ||||
-rw-r--r-- | scene/gui/scroll_container.cpp | 74 | ||||
-rw-r--r-- | scene/gui/scroll_container.h | 6 | ||||
-rw-r--r-- | scene/gui/tab_container.cpp | 161 | ||||
-rw-r--r-- | scene/gui/tab_container.h | 12 | ||||
-rw-r--r-- | scene/gui/tabs.cpp | 118 | ||||
-rw-r--r-- | scene/gui/tabs.h | 7 | ||||
-rw-r--r-- | scene/gui/text_edit.cpp | 84 | ||||
-rw-r--r-- | scene/gui/text_edit.h | 7 |
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; |