diff options
Diffstat (limited to 'scene/gui')
| -rw-r--r-- | scene/gui/check_button.cpp | 23 | ||||
| -rw-r--r-- | scene/gui/check_button.h | 1 | ||||
| -rw-r--r-- | scene/gui/control.cpp | 28 | ||||
| -rw-r--r-- | scene/gui/control.h | 8 | ||||
| -rw-r--r-- | scene/gui/file_dialog.cpp | 28 | ||||
| -rw-r--r-- | scene/gui/item_list.cpp | 10 | ||||
| -rw-r--r-- | scene/gui/line_edit.cpp | 125 | ||||
| -rw-r--r-- | scene/gui/line_edit.h | 17 | ||||
| -rw-r--r-- | scene/gui/nine_patch_rect.cpp (renamed from scene/gui/patch_9_rect.cpp) | 4 | ||||
| -rw-r--r-- | scene/gui/nine_patch_rect.h (renamed from scene/gui/patch_9_rect.h) | 8 | ||||
| -rw-r--r-- | scene/gui/rich_text_label.cpp | 24 | ||||
| -rw-r--r-- | scene/gui/rich_text_label.h | 4 | ||||
| -rw-r--r-- | scene/gui/slider.cpp | 6 | ||||
| -rw-r--r-- | scene/gui/tab_container.cpp | 16 | ||||
| -rw-r--r-- | scene/gui/tabs.cpp | 2 | ||||
| -rw-r--r-- | scene/gui/text_edit.cpp | 283 | ||||
| -rw-r--r-- | scene/gui/text_edit.h | 19 | ||||
| -rw-r--r-- | scene/gui/tree.cpp | 39 | ||||
| -rw-r--r-- | scene/gui/tree.h | 1 | ||||
| -rw-r--r-- | scene/gui/viewport_container.cpp | 35 | ||||
| -rw-r--r-- | scene/gui/viewport_container.h | 4 |
21 files changed, 514 insertions, 171 deletions
diff --git a/scene/gui/check_button.cpp b/scene/gui/check_button.cpp index e68159e27f..77fdedd5e5 100644 --- a/scene/gui/check_button.cpp +++ b/scene/gui/check_button.cpp @@ -32,6 +32,23 @@ #include "print_string.h" #include "servers/visual_server.h" +Size2 CheckButton::get_minimum_size() const { + + Size2 minsize = Button::get_minimum_size(); + + Ref<Texture> on = Control::get_icon("on"); + Ref<Texture> off = Control::get_icon("off"); + Size2 tex_size = Size2(0, 0); + if (!on.is_null()) + tex_size = Size2(on->get_width(), on->get_height()); + if (!off.is_null()) + tex_size = Size2(MAX(tex_size.width, off->get_width()), MAX(tex_size.height, off->get_height())); + minsize += Size2(tex_size.width + get_constant("hseparation"), 0); + minsize.height = MAX(minsize.height, tex_size.height); + + return get_stylebox("normal")->get_minimum_size() + minsize; +} + void CheckButton::_notification(int p_what) { if (p_what == NOTIFICATION_DRAW) { @@ -41,9 +58,11 @@ void CheckButton::_notification(int p_what) { Ref<Texture> on = Control::get_icon("on"); Ref<Texture> off = Control::get_icon("off"); + Ref<StyleBox> sb = get_stylebox("normal"); + Size2 sb_ofs = Size2(sb->get_margin(MARGIN_RIGHT), sb->get_margin(MARGIN_TOP)); Vector2 ofs; - ofs.x = get_size().width - on->get_width(); - ofs.y = int((get_size().height - on->get_height()) / 2); + ofs.x = get_minimum_size().width - (on->get_width() + sb_ofs.width); + ofs.y = sb_ofs.height; if (is_pressed()) on->draw(ci, ofs); diff --git a/scene/gui/check_button.h b/scene/gui/check_button.h index af3b80fe04..eb68943fe7 100644 --- a/scene/gui/check_button.h +++ b/scene/gui/check_button.h @@ -39,6 +39,7 @@ class CheckButton : public Button { GDCLASS(CheckButton, Button); protected: + virtual Size2 get_minimum_size() const; void _notification(int p_what); public: diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 91c5263bf5..e73ada9f31 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -2416,24 +2416,14 @@ float Control::get_rotation() const { return data.rotation; } -void Control::set_rotation_deg(float p_degrees) { +void Control::set_rotation_degrees(float p_degrees) { set_rotation(Math::deg2rad(p_degrees)); } -float Control::get_rotation_deg() const { +float Control::get_rotation_degrees() const { return Math::rad2deg(get_rotation()); } -// Kept for compatibility after rename to {s,g}et_rotation_deg. -// Could be removed after a couple releases. -void Control::_set_rotation_deg(float p_degrees) { - WARN_PRINT("Deprecated method Control._set_rotation_deg(): This method was renamed to set_rotation_deg. Please adapt your code accordingly, as the old method will be obsoleted."); - set_rotation_deg(p_degrees); -} -float Control::_get_rotation_deg() const { - WARN_PRINT("Deprecated method Control._get_rotation_deg(): This method was renamed to get_rotation_deg. Please adapt your code accordingly, as the old method will be obsoleted."); - return get_rotation_deg(); -} //needed to update the control if the font changes.. void Control::_ref_font(Ref<Font> p_sc) { @@ -2606,9 +2596,7 @@ void Control::_bind_methods() { ClassDB::bind_method(D_METHOD("set_custom_minimum_size", "size"), &Control::set_custom_minimum_size); ClassDB::bind_method(D_METHOD("set_global_position", "position"), &Control::set_global_position); ClassDB::bind_method(D_METHOD("set_rotation", "radians"), &Control::set_rotation); - ClassDB::bind_method(D_METHOD("set_rotation_deg", "degrees"), &Control::set_rotation_deg); - // TODO: Obsolete this method (old name) properly (GH-4397) - ClassDB::bind_method(D_METHOD("_set_rotation_deg", "degrees"), &Control::_set_rotation_deg); + ClassDB::bind_method(D_METHOD("set_rotation_degrees", "degrees"), &Control::set_rotation_degrees); ClassDB::bind_method(D_METHOD("set_scale", "scale"), &Control::set_scale); ClassDB::bind_method(D_METHOD("set_pivot_offset", "pivot_offset"), &Control::set_pivot_offset); ClassDB::bind_method(D_METHOD("get_margin", "margin"), &Control::get_margin); @@ -2617,9 +2605,7 @@ void Control::_bind_methods() { ClassDB::bind_method(D_METHOD("get_position"), &Control::get_position); ClassDB::bind_method(D_METHOD("get_size"), &Control::get_size); ClassDB::bind_method(D_METHOD("get_rotation"), &Control::get_rotation); - ClassDB::bind_method(D_METHOD("get_rotation_deg"), &Control::get_rotation_deg); - // TODO: Obsolete this method (old name) properly (GH-4397) - ClassDB::bind_method(D_METHOD("_get_rotation_deg"), &Control::_get_rotation_deg); + ClassDB::bind_method(D_METHOD("get_rotation_degrees"), &Control::get_rotation_degrees); ClassDB::bind_method(D_METHOD("get_scale"), &Control::get_scale); ClassDB::bind_method(D_METHOD("get_pivot_offset"), &Control::get_pivot_offset); ClassDB::bind_method(D_METHOD("get_custom_minimum_size"), &Control::get_custom_minimum_size); @@ -2738,7 +2724,7 @@ void Control::_bind_methods() { ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "rect_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_position", "get_position"); ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "rect_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_size", "get_size"); ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "rect_min_size"), "set_custom_minimum_size", "get_custom_minimum_size"); - ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "rect_rotation", PROPERTY_HINT_RANGE, "-1080,1080,0.01"), "set_rotation_deg", "get_rotation_deg"); + ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "rect_rotation", PROPERTY_HINT_RANGE, "-1080,1080,0.01"), "set_rotation_degrees", "get_rotation_degrees"); ADD_PROPERTYNO(PropertyInfo(Variant::VECTOR2, "rect_scale"), "set_scale", "get_scale"); ADD_PROPERTYNO(PropertyInfo(Variant::VECTOR2, "rect_pivot_offset"), "set_pivot_offset", "get_pivot_offset"); ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "rect_clip_content"), "set_clip_contents", "is_clipping_contents"); @@ -2811,12 +2797,12 @@ void Control::_bind_methods() { BIND_ENUM_CONSTANT(PRESET_WIDE); BIND_ENUM_CONSTANT(PRESET_MODE_MINSIZE); - BIND_ENUM_CONSTANT(PRESET_MODE_KEEP_HEIGHT); BIND_ENUM_CONSTANT(PRESET_MODE_KEEP_WIDTH); + BIND_ENUM_CONSTANT(PRESET_MODE_KEEP_HEIGHT); BIND_ENUM_CONSTANT(PRESET_MODE_KEEP_SIZE); - BIND_ENUM_CONSTANT(SIZE_EXPAND); BIND_ENUM_CONSTANT(SIZE_FILL); + BIND_ENUM_CONSTANT(SIZE_EXPAND); BIND_ENUM_CONSTANT(SIZE_EXPAND_FILL); BIND_ENUM_CONSTANT(SIZE_SHRINK_CENTER); BIND_ENUM_CONSTANT(SIZE_SHRINK_END); diff --git a/scene/gui/control.h b/scene/gui/control.h index 5b146b4454..4d0e3934ad 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -226,10 +226,6 @@ private: void _size_changed(); String _get_tooltip() const; - // Deprecated, should be removed in a future version. - void _set_rotation_deg(float p_degrees); - float _get_rotation_deg() const; - void _ref_font(Ref<Font> p_sc); void _unref_font(Ref<Font> p_sc); void _font_changed(); @@ -332,9 +328,9 @@ public: Rect2 get_window_rect() const; ///< use with care, as it blocks waiting for the visual server void set_rotation(float p_radians); - void set_rotation_deg(float p_degrees); + void set_rotation_degrees(float p_degrees); float get_rotation() const; - float get_rotation_deg() const; + float get_rotation_degrees() const; void set_h_grow_direction(GrowDirection p_direction); GrowDirection get_h_grow_direction() const; diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index d8ff048dfb..6ade4fcc38 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -759,38 +759,42 @@ FileDialog::FileDialog() { mode = MODE_SAVE_FILE; set_title(RTR("Save a File")); + HBoxContainer *hbc = memnew(HBoxContainer); + hbc->add_child(memnew(Label(RTR("Path:")))); dir = memnew(LineEdit); - HBoxContainer *pathhb = memnew(HBoxContainer); - pathhb->add_child(dir); + hbc->add_child(dir); dir->set_h_size_flags(SIZE_EXPAND_FILL); refresh = memnew(ToolButton); refresh->connect("pressed", this, "_update_file_list"); - pathhb->add_child(refresh); + hbc->add_child(refresh); drives = memnew(OptionButton); - pathhb->add_child(drives); + hbc->add_child(drives); drives->connect("item_selected", this, "_select_drive"); makedir = memnew(Button); makedir->set_text(RTR("Create Folder")); makedir->connect("pressed", this, "_make_dir"); - pathhb->add_child(makedir); - - vbc->add_margin_child(RTR("Path:"), pathhb); + hbc->add_child(makedir); + vbc->add_child(hbc); tree = memnew(Tree); tree->set_hide_root(true); vbc->add_margin_child(RTR("Directories & Files:"), tree, true); + hbc = memnew(HBoxContainer); + hbc->add_child(memnew(Label(RTR("File:")))); file = memnew(LineEdit); - //add_child(file); - vbc->add_margin_child(RTR("File:"), file); - + file->set_stretch_ratio(4); + file->set_h_size_flags(SIZE_EXPAND_FILL); + hbc->add_child(file); filter = memnew(OptionButton); - //add_child(filter); - vbc->add_margin_child(RTR("Filter:"), filter); + filter->set_stretch_ratio(3); + filter->set_h_size_flags(SIZE_EXPAND_FILL); filter->set_clip_text(true); //too many extensions overflow it + hbc->add_child(filter); + vbc->add_child(hbc); dir_access = DirAccess::create(DirAccess::ACCESS_RESOURCES); access = ACCESS_RESOURCES; diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index a034c7224f..e9e9dcc859 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -489,7 +489,7 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) { if (mb->get_button_index() == BUTTON_RIGHT) { - emit_signal("item_rmb_selected", i, pos); + emit_signal("item_rmb_selected", i, get_local_mouse_position()); } } else { @@ -500,7 +500,7 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) { if (items[i].selected && mb->get_button_index() == BUTTON_RIGHT) { - emit_signal("item_rmb_selected", i, pos); + emit_signal("item_rmb_selected", i, get_local_mouse_position()); } else { bool selected = !items[i].selected; @@ -515,7 +515,7 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) { if (mb->get_button_index() == BUTTON_RIGHT) { - emit_signal("item_rmb_selected", i, pos); + emit_signal("item_rmb_selected", i, get_local_mouse_position()); } else if (/*select_mode==SELECT_SINGLE &&*/ mb->is_doubleclick()) { emit_signal("item_activated", i); @@ -754,7 +754,7 @@ void ItemList::_notification(int p_what) { int width = size.width - bg->get_minimum_size().width; if (scroll_bar->is_visible()) { - width -= mw + bg->get_margin(MARGIN_RIGHT); + width -= mw; } draw_style_box(bg, Rect2(Point2(), size)); @@ -1107,7 +1107,7 @@ void ItemList::_notification(int p_what) { } for (int i = 0; i < separators.size(); i++) { - draw_line(Vector2(bg->get_margin(MARGIN_LEFT), base_ofs.y + separators[i]), Vector2(size.width - bg->get_margin(MARGIN_RIGHT), base_ofs.y + separators[i]), guide_color); + draw_line(Vector2(bg->get_margin(MARGIN_LEFT), base_ofs.y + separators[i]), Vector2(width, base_ofs.y + separators[i]), guide_color); } } } diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index ed8eff436c..5d3e5ec0e8 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -48,7 +48,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { if (b.is_valid()) { - if (b->is_pressed() && b->get_button_index() == BUTTON_RIGHT) { + 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_size(Vector2(1, 1)); menu->popup(); @@ -161,13 +161,14 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { } break; - case (KEY_Z): { // Simple One level undo - + case (KEY_Z): { // undo / redo if (editable) { - - undo(); + if (k->get_shift()) { + redo(); + } else { + undo(); + } } - } break; case (KEY_U): { // Delete from start to cursor @@ -175,7 +176,6 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { if (editable) { selection_clear(); - undo_text = text; text = text.substr(cursor_pos, text.length() - cursor_pos); Ref<Font> font = get_font("font"); @@ -205,7 +205,6 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { if (editable) { selection_clear(); - undo_text = text; text = text.substr(0, cursor_pos); _text_changed(); } @@ -245,7 +244,6 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { break; if (selection.enabled) { - undo_text = text; selection_delete(); break; } @@ -276,7 +274,6 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { set_cursor_position(cc); } else { - undo_text = text; delete_char(); } @@ -382,7 +379,6 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { } if (selection.enabled) { - undo_text = text; selection_delete(); break; } @@ -417,7 +413,6 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { delete_text(cursor_pos, cc); } else { - undo_text = text; set_cursor_position(cursor_pos + 1); delete_char(); } @@ -778,7 +773,6 @@ void LineEdit::copy_text() { void LineEdit::cut_text() { if (selection.enabled) { - undo_text = text; OS::get_singleton()->set_clipboard(text.substr(selection.begin, selection.end - selection.begin)); selection_delete(); } @@ -798,23 +792,33 @@ void LineEdit::paste_text() { } void LineEdit::undo() { - - int old_cursor_pos = cursor_pos; - text = undo_text; - - Ref<Font> font = get_font("font"); - - cached_width = 0; - for (int i = 0; i < text.length(); i++) - cached_width += font->get_char_size(text[i]).width; - - if (old_cursor_pos > text.length()) { - set_cursor_position(text.length()); - } else { - set_cursor_position(old_cursor_pos); + if (undo_stack_pos == NULL) { + if (undo_stack.size() <= 1) { + return; + } + undo_stack_pos = undo_stack.back(); + } else if (undo_stack_pos == undo_stack.front()) { + return; } + undo_stack_pos = undo_stack_pos->prev(); + TextOperation op = undo_stack_pos->get(); + text = op.text; + set_cursor_position(op.cursor_pos); + _emit_text_change(); +} - _text_changed(); +void LineEdit::redo() { + if (undo_stack_pos == NULL) { + return; + } + if (undo_stack_pos == undo_stack.back()) { + return; + } + undo_stack_pos = undo_stack_pos->next(); + TextOperation op = undo_stack_pos->get(); + text = op.text; + set_cursor_position(op.cursor_pos); + _emit_text_change(); } void LineEdit::shift_selection_check_pre(bool p_shift) { @@ -947,8 +951,6 @@ void LineEdit::delete_char() { void LineEdit::delete_text(int p_from_column, int p_to_column) { - undo_text = text; - if (text.size() > 0) { Ref<Font> font = get_font("font"); if (font != NULL) { @@ -1036,9 +1038,11 @@ void LineEdit::set_cursor_position(int p_pos) { Ref<StyleBox> style = get_stylebox("normal"); Ref<Font> font = get_font("font"); - if (cursor_pos < window_pos) { + if (cursor_pos <= window_pos) { /* Adjust window if cursor goes too much to the left */ - set_window_pos(cursor_pos); + if (window_pos > 0) + set_window_pos(window_pos - 1); + } else if (cursor_pos > window_pos) { /* Adjust window if cursor goes too much to the right */ int window_width = get_size().width - style->get_minimum_size().width; @@ -1086,8 +1090,6 @@ void LineEdit::append_at_cursor(String p_text) { if ((max_length <= 0) || (text.length() + p_text.length() <= max_length)) { - undo_text = text; - Ref<Font> font = get_font("font"); if (font != NULL) { for (int i = 0; i < p_text.length(); i++) @@ -1105,6 +1107,7 @@ void LineEdit::append_at_cursor(String p_text) { void LineEdit::clear_internal() { + _clear_undo_stack(); cached_width = 0; cursor_pos = 0; window_pos = 0; @@ -1275,9 +1278,22 @@ void LineEdit::menu_option(int p_option) { undo(); } } break; + case MENU_REDO: { + if (editable) { + redo(); + } + } } } +void LineEdit::set_context_menu_enabled(bool p_enable) { + context_menu_enabled = p_enable; +} + +bool LineEdit::is_context_menu_enabled() { + return context_menu_enabled; +} + PopupMenu *LineEdit::get_menu() const { return menu; } @@ -1312,10 +1328,43 @@ void LineEdit::_text_changed() { if (expand_to_text_length) minimum_size_changed(); + _emit_text_change(); + _clear_redo(); +} + +void LineEdit::_emit_text_change() { emit_signal("text_changed", text); _change_notify("text"); } +void LineEdit::_clear_redo() { + _create_undo_state(); + if (undo_stack_pos == NULL) { + return; + } + + undo_stack_pos = undo_stack_pos->next(); + while (undo_stack_pos) { + List<TextOperation>::Element *elem = undo_stack_pos; + undo_stack_pos = undo_stack_pos->next(); + undo_stack.erase(elem); + } + _create_undo_state(); +} + +void LineEdit::_clear_undo_stack() { + undo_stack.clear(); + undo_stack_pos = NULL; + _create_undo_state(); +} + +void LineEdit::_create_undo_state() { + TextOperation op; + op.text = text; + op.cursor_pos = cursor_pos; + undo_stack.push_back(op); +} + void LineEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("_toggle_draw_caret"), &LineEdit::_toggle_draw_caret); @@ -1354,6 +1403,8 @@ void LineEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("select", "from", "to"), &LineEdit::select, DEFVAL(0), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("menu_option", "option"), &LineEdit::menu_option); 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); ADD_SIGNAL(MethodInfo("text_changed", PropertyInfo(Variant::STRING, "text"))); ADD_SIGNAL(MethodInfo("text_entered", PropertyInfo(Variant::STRING, "text"))); @@ -1369,6 +1420,7 @@ void LineEdit::_bind_methods() { BIND_ENUM_CONSTANT(MENU_CLEAR); BIND_ENUM_CONSTANT(MENU_SELECT_ALL); BIND_ENUM_CONSTANT(MENU_UNDO); + BIND_ENUM_CONSTANT(MENU_REDO); BIND_ENUM_CONSTANT(MENU_MAX); ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text"); @@ -1384,10 +1436,13 @@ void LineEdit::_bind_methods() { ADD_GROUP("Caret", "caret_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "cursor_set_blink_enabled", "cursor_get_blink_enabled"); ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.1"), "cursor_set_blink_speed", "cursor_get_blink_speed"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled"); } LineEdit::LineEdit() { + undo_stack_pos = NULL; + _create_undo_state(); align = ALIGN_LEFT; cached_width = 0; cursor_pos = 0; @@ -1411,6 +1466,7 @@ LineEdit::LineEdit() { caret_blink_timer->connect("timeout", this, "_toggle_draw_caret"); cursor_set_blink_enabled(false); + context_menu_enabled = true; menu = memnew(PopupMenu); add_child(menu); menu->add_item(TTR("Cut"), MENU_CUT, KEY_MASK_CMD | KEY_X); @@ -1421,6 +1477,7 @@ LineEdit::LineEdit() { menu->add_item(TTR("Clear"), MENU_CLEAR); menu->add_separator(); menu->add_item(TTR("Undo"), MENU_UNDO, KEY_MASK_CMD | KEY_Z); + menu->add_item(TTR("Redo"), MENU_REDO, KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Z); menu->connect("id_pressed", this, "menu_option"); expand_to_text_length = false; } diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index 661f9b60b9..c3a299c2f5 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -56,6 +56,7 @@ public: MENU_CLEAR, MENU_SELECT_ALL, MENU_UNDO, + MENU_REDO, MENU_MAX }; @@ -73,6 +74,7 @@ private: String ime_text; Point2 ime_selection; + bool context_menu_enabled; PopupMenu *menu; int cursor_pos; @@ -92,10 +94,22 @@ private: bool drag_attempt; } selection; + struct TextOperation { + int cursor_pos; + String text; + }; + List<TextOperation> undo_stack; + List<TextOperation>::Element *undo_stack_pos; + + void _clear_undo_stack(); + void _clear_redo(); + void _create_undo_state(); + Timer *caret_blink_timer; static void _ime_text_callback(void *p_self, String p_text, Point2 p_selection); void _text_changed(); + void _emit_text_change(); bool expand_to_text_length; bool caret_blink_enabled; @@ -137,6 +151,8 @@ public: virtual void drop_data(const Point2 &p_point, const Variant &p_data); void menu_option(int p_option); + void set_context_menu_enabled(bool p_enable); + bool is_context_menu_enabled(); PopupMenu *get_menu() const; void select_all(); @@ -166,6 +182,7 @@ public: void cut_text(); void paste_text(); void undo(); + void redo(); void set_editable(bool p_editable); bool is_editable() const; diff --git a/scene/gui/patch_9_rect.cpp b/scene/gui/nine_patch_rect.cpp index 92c34dd3f9..c02d80bba8 100644 --- a/scene/gui/patch_9_rect.cpp +++ b/scene/gui/nine_patch_rect.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* patch_9_rect.cpp */ +/* nine_patch_rect.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -27,7 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "patch_9_rect.h" +#include "nine_patch_rect.h" #include "servers/visual_server.h" diff --git a/scene/gui/patch_9_rect.h b/scene/gui/nine_patch_rect.h index 808b7a1f5d..809daf9db3 100644 --- a/scene/gui/patch_9_rect.h +++ b/scene/gui/nine_patch_rect.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* patch_9_rect.h */ +/* nine_patch_rect.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -27,8 +27,8 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef PATCH_9_FRAME_H -#define PATCH_9_FRAME_H +#ifndef NINE_PATCH_RECT_H +#define NINE_PATCH_RECT_H #include "scene/gui/control.h" /** @@ -81,4 +81,4 @@ public: }; VARIANT_ENUM_CAST(NinePatchRect::AxisStretchMode) -#endif // PATCH_9_FRAME_H +#endif // NINE_PATCH_RECT_H diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index c2ce2a633e..798acb9d52 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -354,7 +354,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & cw = font->get_char_size(c[i], c[i + 1]).x; draw_rect(Rect2(p_ofs.x + pofs, p_ofs.y + y, cw, lh), selection_bg); if (visible) - font->draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - (fh - ascent)), c[i], c[i + 1], selection_fg); + font->draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - (fh - ascent)), c[i], c[i + 1], override_selected_font_color ? selection_fg : color); } else { if (visible) @@ -526,6 +526,9 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & nonblank_line_count += _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, PROCESS_DRAW, cfont, ccolor); } else if (p_mode == PROCESS_POINTER) { _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, PROCESS_POINTER, cfont, ccolor, p_click_pos, r_click_item, r_click_char, r_outside); + if (r_click_item && *r_click_item) { + RETURN; // exit early + } } } @@ -1195,8 +1198,9 @@ void RichTextLabel::add_newline() { return; ItemNewline *item = memnew(ItemNewline); item->line = current_frame->lines.size(); - current_frame->lines.resize(current_frame->lines.size() + 1); _add_item(item, false); + current_frame->lines.resize(current_frame->lines.size() + 1); + _invalidate_current_line(current_frame); } bool RichTextLabel::remove_line(const int p_line) { @@ -1376,6 +1380,16 @@ bool RichTextLabel::is_meta_underlined() const { return underline_meta; } +void RichTextLabel::set_override_selected_font_color(bool p_override_selected_font_color) { + + override_selected_font_color = p_override_selected_font_color; +} + +bool RichTextLabel::is_overriding_selected_font_color() const { + + return override_selected_font_color; +} + void RichTextLabel::set_offset(int p_pixel) { vscroll->set_value(p_pixel); @@ -1906,6 +1920,9 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("set_meta_underline", "enable"), &RichTextLabel::set_meta_underline); ClassDB::bind_method(D_METHOD("is_meta_underlined"), &RichTextLabel::is_meta_underlined); + ClassDB::bind_method(D_METHOD("set_override_selected_font_color", "override"), &RichTextLabel::set_override_selected_font_color); + ClassDB::bind_method(D_METHOD("is_overriding_selected_font_color"), &RichTextLabel::is_overriding_selected_font_color); + ClassDB::bind_method(D_METHOD("set_scroll_active", "active"), &RichTextLabel::set_scroll_active); ClassDB::bind_method(D_METHOD("is_scroll_active"), &RichTextLabel::is_scroll_active); @@ -1948,6 +1965,7 @@ void RichTextLabel::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters", PROPERTY_HINT_RANGE, "-1,128000,1"), "set_visible_characters", "get_visible_characters"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "percent_visible", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_percent_visible", "get_percent_visible"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_selected_font_color"), "set_override_selected_font_color", "is_overriding_selected_font_color"); ADD_SIGNAL(MethodInfo("meta_clicked", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT))); @@ -1970,6 +1988,7 @@ void RichTextLabel::_bind_methods() { BIND_ENUM_CONSTANT(ITEM_ALIGN); BIND_ENUM_CONSTANT(ITEM_INDENT); BIND_ENUM_CONSTANT(ITEM_LIST); + BIND_ENUM_CONSTANT(ITEM_TABLE); BIND_ENUM_CONSTANT(ITEM_META); } @@ -2003,6 +2022,7 @@ RichTextLabel::RichTextLabel() { tab_size = 4; default_align = ALIGN_LEFT; underline_meta = true; + override_selected_font_color = false; scroll_visible = false; scroll_follow = false; diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 24c1e5eb59..f9e37b1094 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -221,6 +221,7 @@ private: int tab_size; bool underline_meta; + bool override_selected_font_color; Align default_align; @@ -313,6 +314,9 @@ public: void set_meta_underline(bool p_underline); bool is_meta_underlined() const; + void set_override_selected_font_color(bool p_override_selected_font_color); + bool is_overriding_selected_font_color() const; + void set_scroll_active(bool p_active); bool is_scroll_active() const; diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp index 116e0ac354..e88742a3e3 100644 --- a/scene/gui/slider.cpp +++ b/scene/gui/slider.cpp @@ -157,6 +157,12 @@ void Slider::_notification(int p_what) { mouse_inside = false; update(); } break; + case NOTIFICATION_VISIBILITY_CHANGED: // fallthrough + case NOTIFICATION_EXIT_TREE: { + + mouse_inside = false; + grab.active = false; + } break; case NOTIFICATION_DRAW: { RID ci = get_canvas_item(); Size2i size = get_size(); diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index 6e50614e8f..581034ddee 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -90,19 +90,28 @@ void TabContainer::_gui_input(const Ref<InputEvent> &p_event) { return; } + // Do not activate tabs when tabs is empty + if (get_tab_count() == 0) + return; + Vector<Control *> tabs = _get_tabs(); // Handle navigation buttons. if (buttons_visible_cache) { + int popup_ofs = 0; + if (popup) { + popup_ofs = menu->get_width(); + } + Ref<Texture> increment = get_icon("increment"); Ref<Texture> decrement = get_icon("decrement"); - if (pos.x > size.width - increment->get_width()) { + if (pos.x > size.width - increment->get_width() - popup_ofs) { if (last_tab_cache < tabs.size() - 1) { first_tab_cache += 1; update(); } return; - } else if (pos.x > size.width - increment->get_width() - decrement->get_width()) { + } else if (pos.x > size.width - increment->get_width() - decrement->get_width() - popup_ofs) { if (first_tab_cache > 0) { first_tab_cache -= 1; update(); @@ -293,6 +302,8 @@ void TabContainer::_notification(int p_what) { } int TabContainer::_get_tab_width(int p_index) const { + + ERR_FAIL_INDEX_V(p_index, get_tab_count(), 0); Control *control = Object::cast_to<Control>(_get_tabs()[p_index]); if (!control || control->is_set_as_toplevel()) return 0; @@ -664,6 +675,7 @@ void TabContainer::_bind_methods() { TabContainer::TabContainer() { first_tab_cache = 0; + last_tab_cache = 0; buttons_visible_cache = false; tabs_ofs_cache = 0; current = 0; diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp index 085f6de6b8..49823e18fc 100644 --- a/scene/gui/tabs.cpp +++ b/scene/gui/tabs.cpp @@ -820,9 +820,9 @@ void Tabs::_bind_methods() { BIND_ENUM_CONSTANT(ALIGN_RIGHT); BIND_ENUM_CONSTANT(ALIGN_MAX); + BIND_ENUM_CONSTANT(CLOSE_BUTTON_SHOW_NEVER); BIND_ENUM_CONSTANT(CLOSE_BUTTON_SHOW_ACTIVE_ONLY); BIND_ENUM_CONSTANT(CLOSE_BUTTON_SHOW_ALWAYS); - BIND_ENUM_CONSTANT(CLOSE_BUTTON_SHOW_NEVER); BIND_ENUM_CONSTANT(CLOSE_BUTTON_MAX); } diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 33c29547be..02b203b5e3 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -376,24 +376,116 @@ void TextEdit::_update_scrollbars() { void TextEdit::_click_selection_held() { if (Input::get_singleton()->is_mouse_button_pressed(BUTTON_LEFT) && selection.selecting_mode != Selection::MODE_NONE) { + switch (selection.selecting_mode) { + case Selection::MODE_POINTER: { + _update_selection_mode_pointer(); + } break; + case Selection::MODE_WORD: { + _update_selection_mode_word(); + } break; + case Selection::MODE_LINE: { + _update_selection_mode_line(); + } break; + default: { + break; + } + } + } else { + click_select_held->stop(); + } +} - Point2 mp = Input::get_singleton()->get_mouse_position() - get_global_position(); +void TextEdit::_update_selection_mode_pointer() { + Point2 mp = Input::get_singleton()->get_mouse_position() - get_global_position(); - int row, col; - _get_mouse_pos(Point2i(mp.x, mp.y), row, col); + int row, col; + _get_mouse_pos(Point2i(mp.x, mp.y), row, col); - select(selection.selecting_line, selection.selecting_column, row, col); + select(selection.selecting_line, selection.selecting_column, row, col); - cursor_set_line(row); - cursor_set_column(col); - update(); + cursor_set_line(row); + cursor_set_column(col); + update(); + + click_select_held->start(); +} - click_select_held->start(); +void TextEdit::_update_selection_mode_word() { + Point2 mp = Input::get_singleton()->get_mouse_position() - get_global_position(); + int row, col; + _get_mouse_pos(Point2i(mp.x, mp.y), row, col); + + String line = text[row]; + int beg = CLAMP(col, 0, line.length()); + // if its the first selection and on whitespace make sure we grab the word instead.. + if (!selection.active) { + while (beg > 0 && line[beg] <= 32) { + beg--; + } + } + int end = beg; + bool symbol = beg < line.length() && _is_symbol(line[beg]); + + // get the word end and begin points + while (beg > 0 && line[beg - 1] > 32 && (symbol == _is_symbol(line[beg - 1]))) { + beg--; + } + while (end < line.length() && line[end + 1] > 32 && (symbol == _is_symbol(line[end + 1]))) { + end++; + } + if (end < line.length()) { + end += 1; + } + + // inital selection + if (!selection.active) { + select(row, beg, row, end); + selection.selecting_column = beg; + selection.selected_word_beg = beg; + selection.selected_word_end = end; + selection.selected_word_origin = beg; + cursor_set_column(selection.to_column); } else { + if ((col <= selection.selected_word_origin && row == selection.selecting_line) || row < selection.selecting_line) { + selection.selecting_column = selection.selected_word_end; + select(row, beg, selection.selecting_line, selection.selected_word_end); + cursor_set_column(selection.from_column); + } else { + selection.selecting_column = selection.selected_word_beg; + select(selection.selecting_line, selection.selected_word_beg, row, end); + cursor_set_column(selection.to_column); + } + } + cursor_set_line(row); - click_select_held->stop(); + update(); + click_select_held->start(); +} + +void TextEdit::_update_selection_mode_line() { + Point2 mp = Input::get_singleton()->get_mouse_position() - get_global_position(); + + int row, col; + _get_mouse_pos(Point2i(mp.x, mp.y), row, col); + + col = 0; + if (row < selection.selecting_line) { + // cursor is above us + cursor_set_line(row - 1); + selection.selecting_column = text[selection.selecting_line].length(); + } else { + // cursor is below us + cursor_set_line(row + 1); + selection.selecting_column = 0; + col = text[row].length(); } + cursor_set_column(0); + + select(selection.selecting_line, selection.selecting_column, row, col); + update(); + + click_select_held->start(); } void TextEdit::_notification(int p_what) { @@ -732,8 +824,17 @@ void TextEdit::_notification(int p_what) { VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg, ofs_y, xmargin_end - xmargin_beg, get_row_height()), cache.mark_color); } - if (line == cursor.line) { - VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(0, ofs_y, xmargin_end, get_row_height()), cache.current_line_color); + if (str.length() == 0) { + // draw line background if empty as we won't loop at at all + if (line == cursor.line && highlight_current_line) { + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(0, ofs_y, xmargin_end, get_row_height()), cache.current_line_color); + } + + // give visual indication of empty selected line + if (selection.active && line >= selection.from_line && line <= selection.to_line) { + int char_w = cache.font->get_char_size(' ').width; + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg, ofs_y, char_w, get_row_height()), cache.selection_color); + } } if (text.is_breakpoint(line) && !draw_breakpoint_gutter) { @@ -764,6 +865,7 @@ void TextEdit::_notification(int p_what) { cache.font->draw(ci, Point2(cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width, ofs_y + cache.font->get_ascent()), fc, cache.line_number_color); } + //loop through charcters in one line for (int j = 0; j < str.length(); j++) { //look for keyword @@ -952,10 +1054,25 @@ void TextEdit::_notification(int p_what) { } } + //current line highlighting bool in_selection = (selection.active && line >= selection.from_line && line <= selection.to_line && (line > selection.from_line || j >= selection.from_column) && (line < selection.to_line || j < selection.to_column)); + if (line == cursor.line && highlight_current_line) { + // if its the first char draw behind line numbers + if (j == 0) { + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(0, ofs_y, (char_ofs + char_margin), get_row_height()), cache.current_line_color); + } + // if its the last char draw to end of the line + if (j == str.length() - 1) { + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(char_ofs + char_margin + char_w, ofs_y, xmargin_end - (char_ofs + char_margin + char_w), get_row_height()), cache.current_line_color); + } + // actual text + if (!in_selection) { + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin, ofs_y), Size2i(char_w, get_row_height())), cache.current_line_color); + } + } + if (in_selection) { - //inside selection! VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin, ofs_y), Size2i(char_w, get_row_height())), cache.selection_color); } @@ -998,7 +1115,7 @@ void TextEdit::_notification(int p_what) { if (brace_open_mismatch) color = cache.brace_mismatch_color; - cache.font->draw_char(ci, Point2i(char_ofs + char_margin, ofs_y + ascent), '_', str[j + 1], in_selection ? cache.font_selected_color : color); + cache.font->draw_char(ci, Point2i(char_ofs + char_margin, ofs_y + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color); } if ( @@ -1007,7 +1124,7 @@ void TextEdit::_notification(int p_what) { if (brace_close_mismatch) color = cache.brace_mismatch_color; - cache.font->draw_char(ci, Point2i(char_ofs + char_margin, ofs_y + ascent), '_', str[j + 1], in_selection ? cache.font_selected_color : color); + cache.font->draw_char(ci, Point2i(char_ofs + char_margin, ofs_y + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color); } } @@ -1066,15 +1183,15 @@ void TextEdit::_notification(int p_what) { } if (str[j] >= 32) { - int w = cache.font->draw_char(ci, Point2i(char_ofs + char_margin, ofs_y + ascent), str[j], str[j + 1], in_selection ? cache.font_selected_color : color); + int w = cache.font->draw_char(ci, Point2i(char_ofs + char_margin, ofs_y + ascent), str[j], str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color); if (underlined) { - draw_rect(Rect2(char_ofs + char_margin, ofs_y + ascent + 2, w, 1), in_selection ? cache.font_selected_color : color); + draw_rect(Rect2(char_ofs + char_margin, ofs_y + ascent + 2, w, 1), in_selection && override_selected_font_color ? cache.font_selected_color : color); } } else if (draw_tabs && str[j] == '\t') { int yofs = (get_row_height() - cache.tab_icon->get_height()) / 2; - cache.tab_icon->draw(ci, Point2(char_ofs + char_margin, ofs_y + yofs), in_selection ? cache.font_selected_color : color); + cache.tab_icon->draw(ci, Point2(char_ofs + char_margin, ofs_y + yofs), in_selection && override_selected_font_color ? cache.font_selected_color : color); } char_ofs += char_w; @@ -1734,36 +1851,15 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { if (!mb->is_doubleclick() && (OS::get_singleton()->get_ticks_msec() - last_dblclk) < 600 && cursor.line == prev_line) { //tripleclick select line - select(cursor.line, 0, cursor.line, text[cursor.line].length()); - selection.selecting_column = 0; + selection.selecting_mode = Selection::MODE_LINE; + _update_selection_mode_line(); last_dblclk = 0; } else if (mb->is_doubleclick() && text[cursor.line].length()) { //doubleclick select world - String s = text[cursor.line]; - int beg = CLAMP(cursor.column, 0, s.length()); - int end = beg; - - if (s[beg] > 32 || beg == s.length()) { - - bool symbol = beg < s.length() && _is_symbol(s[beg]); //not sure if right but most editors behave like this - - while (beg > 0 && s[beg - 1] > 32 && (symbol == _is_symbol(s[beg - 1]))) { - beg--; - } - while (end < s.length() && s[end + 1] > 32 && (symbol == _is_symbol(s[end + 1]))) { - end++; - } - - if (end < s.length()) - end += 1; - - select(cursor.line, beg, cursor.line, end); - - selection.selecting_column = beg; - } - + selection.selecting_mode = Selection::MODE_WORD; + _update_selection_mode_word(); last_dblclk = OS::get_singleton()->get_ticks_msec(); } @@ -1808,21 +1904,21 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } if (mm->get_button_mask() & BUTTON_MASK_LEFT && get_viewport()->gui_get_drag_data() == Variant()) { //ignore if dragging + _reset_caret_blink_timer(); - if (selection.selecting_mode != Selection::MODE_NONE) { - - _reset_caret_blink_timer(); - - int row, col; - _get_mouse_pos(mm->get_position(), row, col); - - select(selection.selecting_line, selection.selecting_column, row, col); - - cursor_set_line(row); - cursor_set_column(col); - update(); - - click_select_held->start(); + switch (selection.selecting_mode) { + case Selection::MODE_POINTER: { + _update_selection_mode_pointer(); + } break; + case Selection::MODE_WORD: { + _update_selection_mode_word(); + } break; + case Selection::MODE_LINE: { + _update_selection_mode_line(); + } break; + default: { + break; + } } } } @@ -2163,7 +2259,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } } } - + begin_complex_operation(); bool first_line = false; if (k->get_command()) { if (k->get_shift()) { @@ -2179,8 +2275,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } } - _insert_text_at_cursor(ins); - _push_current_op(); + insert_text_at_cursor(ins); if (first_line) { cursor_set_line(0); @@ -2188,7 +2283,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { cursor_set_line(cursor.line - 1); cursor_set_column(text[cursor.line].length()); } - + end_complex_operation(); } break; case KEY_ESCAPE: { if (completion_hint != "") { @@ -2823,19 +2918,19 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { if (scancode_handled) accept_event(); /* - if (!scancode_handled && !k->get_command() && !k->get_alt()) { + if (!scancode_handled && !k->get_command() && !k->get_alt()) { if (k->get_unicode()>=32) { - if (readonly) + if (readonly) break; - accept_event(); + accept_event(); } else { - break; + break; + } } - } */ if (k->get_scancode() == KEY_INSERT) { set_insert_mode(!insert_mode); @@ -3227,11 +3322,11 @@ void TextEdit::adjust_viewport_to_cursor() { update(); /* - get_range()->set_max(text.size()); + get_range()->set_max(text.size()); - get_range()->set_page(get_visible_rows()); + get_range()->set_page(get_visible_rows()); - get_range()->set((int)cursor.line_ofs); + get_range()->set((int)cursor.line_ofs); */ } @@ -3573,6 +3668,11 @@ void TextEdit::set_readonly(bool p_readonly) { readonly = p_readonly; } +bool TextEdit::is_readonly() const { + + return readonly; +} + void TextEdit::set_wrap(bool p_wrap) { wrap = p_wrap; @@ -4256,6 +4356,13 @@ bool TextEdit::is_drawing_tabs() const { return draw_tabs; } +void TextEdit::set_override_selected_font_color(bool p_override_selected_font_color) { + override_selected_font_color = p_override_selected_font_color; +} +bool TextEdit::is_overriding_selected_font_color() const { + return override_selected_font_color; +} + void TextEdit::set_insert_mode(bool p_enabled) { insert_mode = p_enabled; update(); @@ -4457,7 +4564,13 @@ void TextEdit::_update_completion_candidates() { completion_index = 0; completion_base = s; Vector<float> sim_cache; + bool single_quote = s.begins_with("'"); + for (int i = 0; i < completion_strings.size(); i++) { + if (single_quote && completion_strings[i].is_quoted()) { + completion_strings[i] = completion_strings[i].unquote().quote("'"); + } + if (s == completion_strings[i]) { // A perfect match, stop completion _cancel_completion(); @@ -4700,6 +4813,15 @@ int TextEdit::get_breakpoint_gutter_width() const { return cache.breakpoint_gutter_width; } +void TextEdit::set_highlight_current_line(bool p_enabled) { + highlight_current_line = p_enabled; + update(); +} + +bool TextEdit::is_highlight_current_line_enabled() const { + return highlight_current_line; +} + bool TextEdit::is_text_field() const { return true; @@ -4749,6 +4871,10 @@ void TextEdit::set_context_menu_enabled(bool p_enable) { context_menu_enabled = p_enable; } +bool TextEdit::is_context_menu_enabled() { + return context_menu_enabled; +} + PopupMenu *TextEdit::get_menu() const { return menu; } @@ -4769,8 +4895,8 @@ void TextEdit::_bind_methods() { BIND_ENUM_CONSTANT(SEARCH_BACKWARDS); /* - ClassDB::bind_method(D_METHOD("delete_char"),&TextEdit::delete_char); - ClassDB::bind_method(D_METHOD("delete_line"),&TextEdit::delete_line); + ClassDB::bind_method(D_METHOD("delete_char"),&TextEdit::delete_char); + ClassDB::bind_method(D_METHOD("delete_line"),&TextEdit::delete_line); */ ClassDB::bind_method(D_METHOD("set_text", "text"), &TextEdit::set_text); @@ -4793,8 +4919,12 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("cursor_is_block_mode"), &TextEdit::cursor_is_block_mode); ClassDB::bind_method(D_METHOD("set_readonly", "enable"), &TextEdit::set_readonly); + ClassDB::bind_method(D_METHOD("is_readonly"), &TextEdit::is_readonly); + ClassDB::bind_method(D_METHOD("set_wrap", "enable"), &TextEdit::set_wrap); ClassDB::bind_method(D_METHOD("set_max_chars", "amount"), &TextEdit::set_max_chars); + ClassDB::bind_method(D_METHOD("set_context_menu_enabled", "enable"), &TextEdit::set_context_menu_enabled); + ClassDB::bind_method(D_METHOD("is_context_menu_enabled"), &TextEdit::is_context_menu_enabled); ClassDB::bind_method(D_METHOD("cut"), &TextEdit::cut); ClassDB::bind_method(D_METHOD("copy"), &TextEdit::copy); @@ -4821,9 +4951,15 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("set_highlight_all_occurrences", "enable"), &TextEdit::set_highlight_all_occurrences); ClassDB::bind_method(D_METHOD("is_highlight_all_occurrences_enabled"), &TextEdit::is_highlight_all_occurrences_enabled); + 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_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); + ClassDB::bind_method(D_METHOD("set_smooth_scroll_enable", "enable"), &TextEdit::set_smooth_scroll_enabled); ClassDB::bind_method(D_METHOD("is_smooth_scroll_enabled"), &TextEdit::is_smooth_scroll_enabled); ClassDB::bind_method(D_METHOD("set_v_scroll_speed", "speed"), &TextEdit::set_v_scroll_speed); @@ -4835,9 +4971,14 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("menu_option", "option"), &TextEdit::menu_option); ClassDB::bind_method(D_METHOD("get_menu"), &TextEdit::get_menu); + 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, "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, "smooth_scrolling"), "set_smooth_scroll_enable", "is_smooth_scroll_enabled"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "v_scroll_speed"), "set_v_scroll_speed", "get_v_scroll_speed"); @@ -4868,6 +5009,7 @@ TextEdit::TextEdit() { readonly = false; setting_row = false; draw_tabs = false; + override_selected_font_color = false; draw_caret = true; max_chars = 0; clear(); @@ -4954,6 +5096,7 @@ TextEdit::TextEdit() { auto_brace_completion_enabled = false; brace_matching_enabled = false; highlight_all_occurrences = false; + highlight_current_line = false; indent_using_spaces = false; space_indent = " "; auto_indent = false; diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 7e61c4e8b1..b90571e0ab 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -51,11 +51,14 @@ class TextEdit : public Control { MODE_NONE, MODE_SHIFT, - MODE_POINTER + MODE_POINTER, + MODE_WORD, + MODE_LINE }; Mode selecting_mode; int selecting_line, selecting_column; + int selected_word_beg, selected_word_end, selected_word_origin; bool selecting_text; bool active; @@ -238,6 +241,7 @@ class TextEdit : public Control { bool setting_row; bool wrap; bool draw_tabs; + bool override_selected_font_color; bool cursor_changed_dirty; bool text_changed_dirty; bool undo_enabled; @@ -252,6 +256,7 @@ class TextEdit : public Control { bool scroll_past_end_of_file_enabled; bool auto_brace_completion_enabled; bool brace_matching_enabled; + bool highlight_current_line; bool auto_indent; bool cut_copy_line; bool insert_mode; @@ -303,6 +308,10 @@ class TextEdit : public Control { void _v_scroll_input(); void _click_selection_held(); + void _update_selection_mode_pointer(); + void _update_selection_mode_word(); + void _update_selection_mode_line(); + void _pre_shift_selection(); void _post_shift_selection(); @@ -439,6 +448,7 @@ public: bool cursor_is_block_mode() const; void set_readonly(bool p_readonly); + bool is_readonly() const; void set_max_chars(int p_max_chars); void set_wrap(bool p_wrap); @@ -482,6 +492,8 @@ public: void set_indent_size(const int p_size); void set_draw_tabs(bool p_draw); bool is_drawing_tabs() const; + void set_override_selected_font_color(bool p_override_selected_font_color); + bool is_overriding_selected_font_color() const; void set_insert_mode(bool p_enabled); bool is_insert_mode() const; @@ -511,6 +523,9 @@ public: 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_guideline(bool p_show); @@ -533,6 +548,8 @@ public: bool is_selecting_identifiers_on_hover_enabled() const; void set_context_menu_enabled(bool p_enable); + bool is_context_menu_enabled(); + PopupMenu *get_menu() const; String get_text_for_completion(); diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 178a1c272b..f2e5919b5f 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -47,18 +47,21 @@ void TreeItem::move_to_top() { } void TreeItem::move_to_bottom() { - if (!parent || !next) return; - while (next) { + TreeItem *prev = get_prev(); + TreeItem *last = next; + while (last->next) + last = last->next; - if (parent->childs == this) - parent->childs = next; - TreeItem *n = next; - next = n->next; - n->next = this; + if (prev) { + prev->next = next; + } else { + parent->childs = next; } + last->next = this; + next = NULL; } Size2 TreeItem::Cell::get_icon_size() const { @@ -1123,7 +1126,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 if (p_item->cells[i].selected && select_mode != SELECT_ROW) { - Rect2i r(item_rect.position, item_rect.size); + Rect2i r(cell_rect.position, cell_rect.size); if (p_item->cells[i].text.size() > 0) { float icon_width = p_item->cells[i].get_icon_size().width; r.position.x += icon_width; @@ -3329,6 +3332,26 @@ Point2 Tree::get_scroll() const { return ofs; } +void Tree::scroll_to_item(TreeItem *p_item) { + + if (!is_visible_in_tree()) { + + // hack to work around crash in get_item_rect() if Tree is not in tree. + return; + } + + // make sure the scrollbar min and max are up to date with latest changes. + update_scrollbars(); + + const Rect2 r = get_item_rect(p_item); + + 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); + } +} + TreeItem *Tree::_search_item_text(TreeItem *p_at, const String &p_find, int *r_col, bool p_selectable, bool p_backwards) { while (p_at) { diff --git a/scene/gui/tree.h b/scene/gui/tree.h index 2ee91a8b73..64d6016942 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -570,6 +570,7 @@ public: TreeItem *search_item_text(const String &p_find, int *r_col = NULL, bool p_selectable = false); Point2 get_scroll() const; + void scroll_to_item(TreeItem *p_item); void set_cursor_can_exit_tree(bool p_enable); bool can_cursor_exit_tree() const; diff --git a/scene/gui/viewport_container.cpp b/scene/gui/viewport_container.cpp index c321b873fd..9244d8de2f 100644 --- a/scene/gui/viewport_container.cpp +++ b/scene/gui/viewport_container.cpp @@ -62,6 +62,34 @@ bool ViewportContainer::is_stretch_enabled() const { return stretch; } +void ViewportContainer::set_stretch_shrink(int p_shrink) { + + ERR_FAIL_COND(p_shrink < 1); + if (shrink == p_shrink) + return; + + shrink = p_shrink; + + if (!stretch) + return; + + for (int i = 0; i < get_child_count(); i++) { + + Viewport *c = Object::cast_to<Viewport>(get_child(i)); + if (!c) + continue; + + c->set_size(get_size() / shrink); + } + + update(); +} + +int ViewportContainer::get_stretch_shrink() const { + + return shrink; +} + void ViewportContainer::_notification(int p_what) { if (p_what == NOTIFICATION_RESIZED) { @@ -75,7 +103,7 @@ void ViewportContainer::_notification(int p_what) { if (!c) continue; - c->set_size(get_size()); + c->set_size(get_size() / shrink); } } @@ -115,10 +143,15 @@ void ViewportContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_stretch", "enable"), &ViewportContainer::set_stretch); ClassDB::bind_method(D_METHOD("is_stretch_enabled"), &ViewportContainer::is_stretch_enabled); + ClassDB::bind_method(D_METHOD("set_stretch_shrink", "amount"), &ViewportContainer::set_stretch_shrink); + ClassDB::bind_method(D_METHOD("get_stretch_shrink"), &ViewportContainer::get_stretch_shrink); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stretch"), "set_stretch", "is_stretch_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "stretch_shrink"), "set_stretch_shrink", "get_stretch_shrink"); } ViewportContainer::ViewportContainer() { stretch = false; + shrink = 1; } diff --git a/scene/gui/viewport_container.h b/scene/gui/viewport_container.h index 630523b5fb..ebf5869ed9 100644 --- a/scene/gui/viewport_container.h +++ b/scene/gui/viewport_container.h @@ -37,6 +37,7 @@ class ViewportContainer : public Container { GDCLASS(ViewportContainer, Container); bool stretch; + int shrink; protected: void _notification(int p_what); @@ -46,6 +47,9 @@ public: void set_stretch(bool p_enable); bool is_stretch_enabled() const; + void set_stretch_shrink(int p_shrink); + int get_stretch_shrink() const; + virtual Size2 get_minimum_size() const; ViewportContainer(); |