Browse Source

Add clear text button to LineEdit

- Add pressed state to clear button
- Enable clear button on all inputs with search icon
- Remove duplicate clear buttons
- Fix rendering of icon for center and right alignments
- Add clear button to more search fields
- Add clear icon to default theme
- Add method to control enabled state of clear button
- Add property to enable clear button from inspector
Łukasz Rutkowski 7 years ago
parent
commit
e8a435c8cd

+ 2 - 0
editor/create_dialog.cpp

@@ -425,6 +425,8 @@ void CreateDialog::_notification(int p_what) {
 	switch (p_what) {
 		case NOTIFICATION_ENTER_TREE: {
 			connect("confirmed", this, "_confirmed");
+			search_box->add_icon_override("right_icon", get_icon("Search", "EditorIcons"));
+			search_box->set_clear_button_enabled(true);
 			favorite->set_icon(get_icon("Favorites", "EditorIcons"));
 		} break;
 		case NOTIFICATION_EXIT_TREE: {

+ 4 - 0
editor/editor_help.cpp

@@ -254,6 +254,7 @@ void EditorHelpSearch::_notification(int p_what) {
 
 		//_update_icons
 		search_box->add_icon_override("right_icon", get_icon("Search", "EditorIcons"));
+		search_box->set_clear_button_enabled(true);
 
 		connect("confirmed", this, "_confirmed");
 		_update_search();
@@ -268,6 +269,7 @@ void EditorHelpSearch::_notification(int p_what) {
 
 		//_update_icons
 		search_box->add_icon_override("right_icon", get_icon("Search", "EditorIcons"));
+		search_box->set_clear_button_enabled(true);
 	} else if (p_what == NOTIFICATION_PROCESS) {
 
 		if (search.is_valid()) {
@@ -382,6 +384,7 @@ void EditorHelpIndex::_notification(int p_what) {
 
 		//_update_icons
 		search_box->add_icon_override("right_icon", get_icon("Search", "EditorIcons"));
+		search_box->set_clear_button_enabled(true);
 		_update_class_list();
 
 		connect("confirmed", this, "_tree_item_selected");
@@ -393,6 +396,7 @@ void EditorHelpIndex::_notification(int p_what) {
 
 		//_update_icons
 		search_box->add_icon_override("right_icon", get_icon("Search", "EditorIcons"));
+		search_box->set_clear_button_enabled(true);
 	}
 }
 

+ 2 - 0
editor/editor_themes.cpp

@@ -828,6 +828,8 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
 	theme->set_color("font_color", "LineEdit", font_color);
 	theme->set_color("cursor_color", "LineEdit", font_color);
 	theme->set_color("selection_color", "LineEdit", font_color_selection);
+	theme->set_color("clear_button_color", "LineEdit", font_color);
+	theme->set_color("clear_button_color_pressed", "LineEdit", accent_color);
 
 	// TextEdit
 	theme->set_stylebox("normal", "TextEdit", style_widget);

+ 2 - 0
editor/filesystem_dock.cpp

@@ -197,6 +197,7 @@ void FileSystemDock::_notification(int p_what) {
 			button_hist_next->connect("pressed", this, "_fw_history");
 			button_hist_prev->connect("pressed", this, "_bw_history");
 			search_box->add_icon_override("right_icon", get_icon("Search", ei));
+			search_box->set_clear_button_enabled(true);
 
 			button_hist_next->set_icon(get_icon("Forward", ei));
 			button_hist_prev->set_icon(get_icon("Back", ei));
@@ -253,6 +254,7 @@ void FileSystemDock::_notification(int p_what) {
 			button_hist_prev->set_icon(get_icon("Back", ei));
 
 			search_box->add_icon_override("right_icon", get_icon("Search", ei));
+			search_box->set_clear_button_enabled(true);
 
 			if (new_mode != display_mode) {
 				set_display_mode(new_mode);

+ 2 - 0
editor/groups_editor.cpp

@@ -288,7 +288,9 @@ void GroupDialog::_notification(int p_what) {
 			remove_button->set_icon(get_icon("Back", "EditorIcons"));
 
 			add_filter->add_icon_override("right_icon", get_icon("Search", "EditorIcons"));
+			add_filter->set_clear_button_enabled(true);
 			remove_filter->add_icon_override("right_icon", get_icon("Search", "EditorIcons"));
+			remove_filter->set_clear_button_enabled(true);
 		} break;
 	}
 }

+ 1 - 0
editor/inspector_dock.cpp

@@ -530,6 +530,7 @@ InspectorDock::InspectorDock(EditorNode *p_editor, EditorData &p_editor_data) {
 	search->set_h_size_flags(Control::SIZE_EXPAND_FILL);
 	search->set_placeholder(TTR("Filter properties"));
 	search->add_icon_override("right_icon", get_icon("Search", "EditorIcons"));
+	search->set_clear_button_enabled(true);
 	add_child(search);
 
 	warning = memnew(Button);

+ 4 - 0
editor/plugins/asset_library_editor_plugin.cpp

@@ -554,6 +554,8 @@ void EditorAssetLibrary::_notification(int p_what) {
 
 			error_tr->set_texture(get_icon("Error", "EditorIcons"));
 			reverse->set_icon(get_icon("Sort", "EditorIcons"));
+			filter->add_icon_override("right_icon", get_icon("Search", "EditorIcons"));
+			filter->set_clear_button_enabled(true);
 
 			error_label->raise();
 		} break;
@@ -604,6 +606,8 @@ void EditorAssetLibrary::_notification(int p_what) {
 			library_scroll_bg->add_style_override("panel", get_stylebox("bg", "Tree"));
 			error_tr->set_texture(get_icon("Error", "EditorIcons"));
 			reverse->set_icon(get_icon("Sort", "EditorIcons"));
+			filter->add_icon_override("right_icon", get_icon("Search", "EditorIcons"));
+			filter->set_clear_button_enabled(true);
 		} break;
 	}
 }

+ 3 - 0
editor/plugins/script_editor_plugin.cpp

@@ -219,6 +219,9 @@ void ScriptEditorQuickOpen::_notification(int p_what) {
 		case NOTIFICATION_ENTER_TREE: {
 
 			connect("confirmed", this, "_confirmed");
+
+			search_box->add_icon_override("right_icon", get_icon("Search", "EditorIcons"));
+			search_box->set_clear_button_enabled(true);
 		} break;
 	}
 }

+ 1 - 0
editor/plugins/tile_map_editor_plugin.cpp

@@ -74,6 +74,7 @@ void TileMapEditor::_notification(int p_what) {
 			rotate_270->set_icon(get_icon("Rotate270", "EditorIcons"));
 
 			search_box->add_icon_override("right_icon", get_icon("Search", "EditorIcons"));
+			search_box->set_clear_button_enabled(true);
 
 			PopupMenu *p = options->get_popup();
 			p->set_item_icon(p->get_item_index(OPTION_PAINTING), get_icon("Edit", "EditorIcons"));

+ 4 - 19
editor/project_manager.cpp

@@ -2023,18 +2023,6 @@ void ProjectListFilter::_setup_filters() {
 	filter_option->add_item(TTR("Path"));
 }
 
-void ProjectListFilter::_command(int p_command) {
-	switch (p_command) {
-
-		case CMD_CLEAR_FILTER: {
-			if (search_box->get_text() != "") {
-				search_box->clear();
-				emit_signal("filter_changed");
-			}
-		} break;
-	}
-}
-
 void ProjectListFilter::_search_text_changed(const String &p_newtext) {
 	emit_signal("filter_changed");
 }
@@ -2057,13 +2045,14 @@ void ProjectListFilter::_filter_option_selected(int p_idx) {
 
 void ProjectListFilter::_notification(int p_what) {
 
-	if (p_what == NOTIFICATION_ENTER_TREE)
-		clear_search_button->set_icon(get_icon("Close", "EditorIcons"));
+	if (p_what == NOTIFICATION_ENTER_TREE) {
+		search_box->add_icon_override("right_icon", get_icon("Search", "EditorIcons"));
+		search_box->set_clear_button_enabled(true);
+	}
 }
 
 void ProjectListFilter::_bind_methods() {
 
-	ClassDB::bind_method(D_METHOD("_command"), &ProjectListFilter::_command);
 	ClassDB::bind_method(D_METHOD("_search_text_changed"), &ProjectListFilter::_search_text_changed);
 	ClassDB::bind_method(D_METHOD("_filter_option_selected"), &ProjectListFilter::_filter_option_selected);
 
@@ -2088,8 +2077,4 @@ ProjectListFilter::ProjectListFilter() {
 	search_box->connect("text_changed", this, "_search_text_changed");
 	search_box->set_h_size_flags(SIZE_EXPAND_FILL);
 	add_child(search_box);
-
-	clear_search_button = memnew(ToolButton);
-	clear_search_button->connect("pressed", this, "_command", make_binds(CMD_CLEAR_FILTER));
-	add_child(clear_search_button);
 }

+ 0 - 6
editor/project_manager.h

@@ -128,13 +128,8 @@ class ProjectListFilter : public HBoxContainer {
 private:
 	friend class ProjectManager;
 
-	enum Command {
-		CMD_CLEAR_FILTER,
-	};
-
 	OptionButton *filter_option;
 	LineEdit *search_box;
-	ToolButton *clear_search_button;
 
 	enum FilterOption {
 		FILTER_NAME,
@@ -142,7 +137,6 @@ private:
 	};
 	FilterOption _current_filter;
 
-	void _command(int p_command);
 	void _search_text_changed(const String &p_newtext);
 	void _setup_filters();
 	void _filter_option_selected(int p_idx);

+ 4 - 16
editor/project_settings_editor.cpp

@@ -81,7 +81,8 @@ void ProjectSettingsEditor::_notification(int p_what) {
 			globals_editor->edit(ProjectSettings::get_singleton());
 
 			search_button->set_icon(get_icon("Search", "EditorIcons"));
-			clear_button->set_icon(get_icon("Close", "EditorIcons"));
+			search_box->add_icon_override("right_icon", get_icon("Search", "EditorIcons"));
+			search_box->set_clear_button_enabled(true);
 
 			action_add_error->add_color_override("font_color", get_color("error_color", "Editor"));
 
@@ -119,7 +120,8 @@ void ProjectSettingsEditor::_notification(int p_what) {
 		case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
 
 			search_button->set_icon(get_icon("Search", "EditorIcons"));
-			clear_button->set_icon(get_icon("Close", "EditorIcons"));
+			search_box->add_icon_override("right_icon", get_icon("Search", "EditorIcons"));
+			search_box->set_clear_button_enabled(true);
 			action_add_error->add_color_override("font_color", get_color("error_color", "Editor"));
 			popup_add->set_item_icon(popup_add->get_item_index(INPUT_KEY), get_icon("Keyboard", "EditorIcons"));
 			popup_add->set_item_icon(popup_add->get_item_index(INPUT_JOY_BUTTON), get_icon("JoyButton", "EditorIcons"));
@@ -1591,15 +1593,6 @@ void ProjectSettingsEditor::_toggle_search_bar(bool p_pressed) {
 	}
 }
 
-void ProjectSettingsEditor::_clear_search_box() {
-
-	if (search_box->get_text() == "")
-		return;
-
-	search_box->clear();
-	globals_editor->get_inspector()->update_tree();
-}
-
 void ProjectSettingsEditor::set_plugins_page() {
 
 	tab_container->set_current_tab(plugin_settings->get_index());
@@ -1662,7 +1655,6 @@ void ProjectSettingsEditor::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("_translation_filter_option_changed"), &ProjectSettingsEditor::_translation_filter_option_changed);
 	ClassDB::bind_method(D_METHOD("_translation_filter_mode_changed"), &ProjectSettingsEditor::_translation_filter_mode_changed);
 
-	ClassDB::bind_method(D_METHOD("_clear_search_box"), &ProjectSettingsEditor::_clear_search_box);
 	ClassDB::bind_method(D_METHOD("_toggle_search_bar"), &ProjectSettingsEditor::_toggle_search_bar);
 
 	ClassDB::bind_method(D_METHOD("_copy_to_platform_about_to_show"), &ProjectSettingsEditor::_copy_to_platform_about_to_show);
@@ -1753,10 +1745,6 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
 	search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL);
 	search_bar->add_child(search_box);
 
-	clear_button = memnew(ToolButton);
-	search_bar->add_child(clear_button);
-	clear_button->connect("pressed", this, "_clear_search_box");
-
 	globals_editor = memnew(SectionedInspector);
 	props_base->add_child(globals_editor);
 	globals_editor->get_inspector()->set_undo_redo(EditorNode::get_singleton()->get_undo_redo());

+ 0 - 2
editor/project_settings_editor.h

@@ -69,7 +69,6 @@ class ProjectSettingsEditor : public AcceptDialog {
 	HBoxContainer *search_bar;
 	Button *search_button;
 	LineEdit *search_box;
-	ToolButton *clear_button;
 
 	HBoxContainer *add_prop_bar;
 	AcceptDialog *message;
@@ -158,7 +157,6 @@ class ProjectSettingsEditor : public AcceptDialog {
 	void _translation_filter_mode_changed(int p_mode);
 
 	void _toggle_search_bar(bool p_pressed);
-	void _clear_search_box();
 
 	void _copy_to_platform_about_to_show();
 

+ 3 - 0
editor/quick_open.cpp

@@ -258,6 +258,9 @@ void EditorQuickOpen::_notification(int p_what) {
 	if (p_what == NOTIFICATION_ENTER_TREE) {
 
 		connect("confirmed", this, "_confirmed");
+
+		search_box->add_icon_override("right_icon", get_icon("Search", "EditorIcons"));
+		search_box->set_clear_button_enabled(true);
 	}
 }
 

+ 2 - 0
editor/scene_tree_dock.cpp

@@ -863,6 +863,7 @@ void SceneTreeDock::_notification(int p_what) {
 			button_clear_script->set_icon(get_icon("ScriptRemove", "EditorIcons"));
 
 			filter->add_icon_override("right_icon", get_icon("Search", "EditorIcons"));
+			filter->set_clear_button_enabled(true);
 
 			EditorNode::get_singleton()->get_editor_selection()->connect("selection_changed", this, "_selection_changed");
 
@@ -911,6 +912,7 @@ void SceneTreeDock::_notification(int p_what) {
 			button_clear_script->set_icon(get_icon("ScriptRemove", "EditorIcons"));
 
 			filter->add_icon_override("right_icon", get_icon("Search", "EditorIcons"));
+			filter->set_clear_button_enabled(true);
 		} break;
 		case NOTIFICATION_PROCESS: {
 

+ 2 - 28
editor/settings_config_dialog.cpp

@@ -114,22 +114,6 @@ void EditorSettingsDialog::popup_edit_settings() {
 	_focus_current_search_box();
 }
 
-void EditorSettingsDialog::_clear_search_box() {
-
-	if (search_box->get_text() == "")
-		return;
-
-	search_box->clear();
-	inspector->get_inspector()->update_tree();
-}
-
-void EditorSettingsDialog::_clear_shortcut_search_box() {
-	if (shortcut_search_box->get_text() == "")
-		return;
-
-	shortcut_search_box->clear();
-}
-
 void EditorSettingsDialog::_filter_shortcuts(const String &p_filter) {
 	shortcut_filter = p_filter;
 	_update_shortcuts();
@@ -199,9 +183,9 @@ void EditorSettingsDialog::_unhandled_input(const Ref<InputEvent> &p_event) {
 void EditorSettingsDialog::_update_icons() {
 
 	search_box->add_icon_override("right_icon", get_icon("Search", "EditorIcons"));
+	search_box->set_clear_button_enabled(true);
 	shortcut_search_box->add_icon_override("right_icon", get_icon("Search", "EditorIcons"));
-	clear_button->set_icon(get_icon("Close", "EditorIcons"));
-	shortcut_clear_button->set_icon(get_icon("Close", "EditorIcons"));
+	shortcut_search_box->set_clear_button_enabled(true);
 
 	restart_close_button->set_icon(get_icon("Close", "EditorIcons"));
 	restart_container->add_style_override("panel", get_stylebox("bg", "Tree"));
@@ -411,8 +395,6 @@ void EditorSettingsDialog::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("_settings_save"), &EditorSettingsDialog::_settings_save);
 	ClassDB::bind_method(D_METHOD("_settings_changed"), &EditorSettingsDialog::_settings_changed);
 	ClassDB::bind_method(D_METHOD("_settings_property_edited"), &EditorSettingsDialog::_settings_property_edited);
-	ClassDB::bind_method(D_METHOD("_clear_search_box"), &EditorSettingsDialog::_clear_search_box);
-	ClassDB::bind_method(D_METHOD("_clear_shortcut_search_box"), &EditorSettingsDialog::_clear_shortcut_search_box);
 	ClassDB::bind_method(D_METHOD("_shortcut_button_pressed"), &EditorSettingsDialog::_shortcut_button_pressed);
 	ClassDB::bind_method(D_METHOD("_filter_shortcuts"), &EditorSettingsDialog::_filter_shortcuts);
 	ClassDB::bind_method(D_METHOD("_update_shortcuts"), &EditorSettingsDialog::_update_shortcuts);
@@ -451,10 +433,6 @@ EditorSettingsDialog::EditorSettingsDialog() {
 	search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL);
 	hbc->add_child(search_box);
 
-	clear_button = memnew(ToolButton);
-	hbc->add_child(clear_button);
-	clear_button->connect("pressed", this, "_clear_search_box");
-
 	inspector = memnew(SectionedInspector);
 	//inspector->hide_top_label();
 	inspector->get_inspector()->set_use_filter(true);
@@ -500,10 +478,6 @@ EditorSettingsDialog::EditorSettingsDialog() {
 	hbc->add_child(shortcut_search_box);
 	shortcut_search_box->connect("text_changed", this, "_filter_shortcuts");
 
-	shortcut_clear_button = memnew(ToolButton);
-	hbc->add_child(shortcut_clear_button);
-	shortcut_clear_button->connect("pressed", this, "_clear_shortcut_search_box");
-
 	shortcuts = memnew(Tree);
 	tab_shortcuts->add_child(shortcuts, true);
 	shortcuts->set_v_size_flags(SIZE_EXPAND_FILL);

+ 0 - 2
editor/settings_config_dialog.h

@@ -52,8 +52,6 @@ class EditorSettingsDialog : public AcceptDialog {
 
 	LineEdit *search_box;
 	LineEdit *shortcut_search_box;
-	ToolButton *clear_button;
-	ToolButton *shortcut_clear_button;
 	SectionedInspector *inspector;
 
 	Timer *timer;

+ 1 - 0
modules/visual_script/visual_script_editor.cpp

@@ -3033,6 +3033,7 @@ void VisualScriptEditor::_notification(int p_what) {
 
 	if (p_what == NOTIFICATION_READY) {
 		node_filter->add_icon_override("right_icon", Control::get_icon("Search", "EditorIcons"));
+		node_filter->set_clear_button_enabled(true);
 		variable_editor->connect("changed", this, "_update_members");
 		signal_editor->connect("changed", this, "_update_members");
 

+ 75 - 5
scene/gui/line_edit.cpp

@@ -66,6 +66,12 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
 		_reset_caret_blink_timer();
 		if (b->is_pressed()) {
 
+			if (!text.empty() && is_editable() && _is_over_clear_button(b->get_position())) {
+				clear_button_status.press_attempt = true;
+				clear_button_status.pressing_inside = true;
+				return;
+			}
+
 			shift_selection_check_pre(b->get_shift());
 
 			set_cursor_at_pixel_pos(b->get_position().x);
@@ -102,6 +108,15 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
 
 		} else {
 
+			if (!text.empty() && is_editable() && clear_button_enabled) {
+				bool press_attempt = clear_button_status.press_attempt;
+				clear_button_status.press_attempt = false;
+				if (press_attempt && clear_button_status.pressing_inside && _is_over_clear_button(b->get_position())) {
+					clear();
+					return;
+				}
+			}
+
 			if ((!selection.creating) && (!selection.doubleclick)) {
 				deselect();
 			}
@@ -119,6 +134,14 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
 
 	if (m.is_valid()) {
 
+		if (!text.empty() && is_editable() && clear_button_enabled) {
+			bool last_press_inside = clear_button_status.pressing_inside;
+			clear_button_status.pressing_inside = clear_button_status.press_attempt && _is_over_clear_button(m->get_position());
+			if (last_press_inside != clear_button_status.pressing_inside) {
+				update();
+			}
+		}
+
 		if (m->get_button_mask() & BUTTON_LEFT) {
 
 			if (selection.creating) {
@@ -550,6 +573,25 @@ void LineEdit::drop_data(const Point2 &p_point, const Variant &p_data) {
 	}
 }
 
+Control::CursorShape LineEdit::get_cursor_shape(const Point2 &p_pos) const {
+	if (!text.empty() && is_editable() && _is_over_clear_button(p_pos)) {
+		return CURSOR_ARROW;
+	}
+	return Control::get_cursor_shape(p_pos);
+}
+
+bool LineEdit::_is_over_clear_button(const Point2 &p_pos) const {
+	if (!clear_button_enabled || !has_point(p_pos)) {
+		return false;
+	}
+	Ref<Texture> icon = Control::get_icon("clear");
+	int x_ofs = get_stylebox("normal")->get_offset().x;
+	if (p_pos.x > get_size().width - icon->get_width() - x_ofs) {
+		return true;
+	}
+	return false;
+}
+
 void LineEdit::_notification(int p_what) {
 
 	switch (p_what) {
@@ -657,10 +699,26 @@ void LineEdit::_notification(int p_what) {
 				font_color.a *= placeholder_alpha;
 			font_color.a *= disabled_alpha;
 
-			if (has_icon("right_icon")) {
-				Ref<Texture> r_icon = Control::get_icon("right_icon");
-				ofs_max -= r_icon->get_width();
-				r_icon->draw(ci, Point2(width - r_icon->get_width() - x_ofs, height / 2 - r_icon->get_height() / 2), Color(1, 1, 1, disabled_alpha * .9));
+			bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled;
+			if (has_icon("right_icon") || display_clear_icon) {
+				Ref<Texture> r_icon = Control::get_icon(display_clear_icon ? "clear" : "right_icon");
+				Color color_icon(1, 1, 1, disabled_alpha * .9);
+				if (display_clear_icon) {
+					if (clear_button_status.press_attempt && clear_button_status.pressing_inside) {
+						color_icon = get_color("clear_button_color_pressed");
+					} else {
+						color_icon = get_color("clear_button_color");
+					}
+				}
+				r_icon->draw(ci, Point2(width - r_icon->get_width() - style->get_margin(MARGIN_RIGHT), height / 2 - r_icon->get_height() / 2), color_icon);
+
+				if (align == ALIGN_CENTER) {
+					if (window_pos == 0) {
+						x_ofs = MAX(style->get_margin(MARGIN_LEFT), int(size.width - cached_text_width - r_icon->get_width() - style->get_margin(MARGIN_RIGHT) * 2) / 2);
+					}
+				} else {
+					x_ofs = MAX(style->get_margin(MARGIN_LEFT), x_ofs - r_icon->get_width() - style->get_margin(MARGIN_RIGHT));
+				}
 			}
 
 			int caret_height = font->get_height() > y_area ? y_area : font->get_height();
@@ -1385,10 +1443,18 @@ void LineEdit::set_expand_to_text_length(bool p_enabled) {
 }
 
 bool LineEdit::get_expand_to_text_length() const {
-
 	return expand_to_text_length;
 }
 
+void LineEdit::set_clear_button_enabled(bool p_enabled) {
+	clear_button_enabled = p_enabled;
+	update();
+}
+
+bool LineEdit::is_clear_button_enabled() const {
+	return clear_button_enabled;
+}
+
 void LineEdit::_ime_text_callback(void *p_self, String p_text, Point2 p_selection) {
 	LineEdit *self = (LineEdit *)p_self;
 	self->ime_text = p_text;
@@ -1481,6 +1547,8 @@ void LineEdit::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_menu"), &LineEdit::get_menu);
 	ClassDB::bind_method(D_METHOD("set_context_menu_enabled", "enable"), &LineEdit::set_context_menu_enabled);
 	ClassDB::bind_method(D_METHOD("is_context_menu_enabled"), &LineEdit::is_context_menu_enabled);
+	ClassDB::bind_method(D_METHOD("set_clear_button_enabled", "enable"), &LineEdit::set_clear_button_enabled);
+	ClassDB::bind_method(D_METHOD("is_clear_button_enabled"), &LineEdit::is_clear_button_enabled);
 
 	ADD_SIGNAL(MethodInfo("text_changed", PropertyInfo(Variant::STRING, "new_text")));
 	ADD_SIGNAL(MethodInfo("text_entered", PropertyInfo(Variant::STRING, "new_text")));
@@ -1508,6 +1576,7 @@ void LineEdit::_bind_methods() {
 	ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "expand_to_text_length"), "set_expand_to_text_length", "get_expand_to_text_length");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_focus_mode", "get_focus_mode");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clear_button_enabled"), "set_clear_button_enabled", "is_clear_button_enabled");
 	ADD_GROUP("Placeholder", "placeholder_");
 	ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "placeholder_text"), "set_placeholder", "get_placeholder");
 	ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "placeholder_alpha", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_placeholder_alpha", "get_placeholder_alpha");
@@ -1532,6 +1601,7 @@ LineEdit::LineEdit() {
 	secret_character = "*";
 	text_changed_dirty = false;
 	placeholder_alpha = 0.6;
+	clear_button_enabled = false;
 
 	deselect();
 	set_focus_mode(FOCUS_ALL);

+ 14 - 0
scene/gui/line_edit.h

@@ -87,6 +87,8 @@ private:
 	int cached_width;
 	int cached_placeholder_width;
 
+	bool clear_button_enabled;
+
 	struct Selection {
 
 		int begin;
@@ -105,6 +107,13 @@ private:
 	List<TextOperation> undo_stack;
 	List<TextOperation>::Element *undo_stack_pos;
 
+	struct ClearButtonStatus {
+		bool press_attempt;
+		bool pressing_inside;
+	} clear_button_status;
+
+	bool _is_over_clear_button(const Point2 &p_pos) const;
+
 	void _clear_undo_stack();
 	void _clear_redo();
 	void _create_undo_state();
@@ -150,6 +159,8 @@ public:
 	virtual bool can_drop_data(const Point2 &p_point, const Variant &p_data) const;
 	virtual void drop_data(const Point2 &p_point, const Variant &p_data);
 
+	virtual CursorShape get_cursor_shape(const Point2 &p_pos) const;
+
 	void menu_option(int p_option);
 	void set_context_menu_enabled(bool p_enable);
 	bool is_context_menu_enabled();
@@ -201,6 +212,9 @@ public:
 	void set_expand_to_text_length(bool p_enabled);
 	bool get_expand_to_text_length() const;
 
+	void set_clear_button_enabled(bool p_enabled);
+	bool is_clear_button_enabled() const;
+
 	virtual bool is_text_field() const;
 	LineEdit();
 	~LineEdit();

+ 4 - 0
scene/resources/default_theme/default_theme.cpp

@@ -434,9 +434,13 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
 	theme->set_color("font_color_selected", "LineEdit", Color(0, 0, 0));
 	theme->set_color("cursor_color", "LineEdit", control_font_color_hover);
 	theme->set_color("selection_color", "LineEdit", font_color_selection);
+	theme->set_color("clear_button_color", "LineEdit", control_font_color);
+	theme->set_color("clear_button_color_pressed", "LineEdit", control_font_color_pressed);
 
 	theme->set_constant("minimum_spaces", "LineEdit", 12 * scale);
 
+	theme->set_icon("clear", "LineEdit", make_icon(line_edit_clear_png));
+
 	// ProgressBar
 
 	theme->set_stylebox("bg", "ProgressBar", make_stylebox(progress_bar_png, 4, 4, 4, 4, 0, 0, 0, 0));

BIN
scene/resources/default_theme/line_edit_clear.png


File diff suppressed because it is too large
+ 2 - 2
scene/resources/default_theme/theme_data.h


Some files were not shown because too many files changed in this diff