1 /*************************************************************************/
2 /*  settings_config_dialog.cpp                                           */
3 /*************************************************************************/
4 /*                       This file is part of:                           */
5 /*                           GODOT ENGINE                                */
6 /*                      https://godotengine.org                          */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
9 /* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
10 /*                                                                       */
11 /* Permission is hereby granted, free of charge, to any person obtaining */
12 /* a copy of this software and associated documentation files (the       */
13 /* "Software"), to deal in the Software without restriction, including   */
14 /* without limitation the rights to use, copy, modify, merge, publish,   */
15 /* distribute, sublicense, and/or sell copies of the Software, and to    */
16 /* permit persons to whom the Software is furnished to do so, subject to */
17 /* the following conditions:                                             */
18 /*                                                                       */
19 /* The above copyright notice and this permission notice shall be        */
20 /* included in all copies or substantial portions of the Software.       */
21 /*                                                                       */
22 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
23 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
24 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
25 /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
26 /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
27 /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
28 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
29 /*************************************************************************/
30 #include "settings_config_dialog.h"
31 #include "editor_file_system.h"
32 #include "editor_node.h"
33 #include "editor_settings.h"
34 #include "globals.h"
35 #include "os/keyboard.h"
36 #include "scene/gui/margin_container.h"
37 
ok_pressed()38 void EditorSettingsDialog::ok_pressed() {
39 
40 	if (!EditorSettings::get_singleton())
41 		return;
42 
43 	_settings_save();
44 	timer->stop();
45 }
46 
_settings_changed()47 void EditorSettingsDialog::_settings_changed() {
48 
49 	timer->start();
50 }
51 
_settings_property_edited(const String & p_name)52 void EditorSettingsDialog::_settings_property_edited(const String &p_name) {
53 
54 	String full_name = property_editor->get_full_item_path(p_name);
55 
56 	// Small usability workaround to update the text color settings when the
57 	// color theme is changed
58 	if (full_name == "text_editor/color_theme") {
59 		property_editor->get_property_editor()->update_tree();
60 	}
61 }
62 
_settings_save()63 void EditorSettingsDialog::_settings_save() {
64 
65 	EditorSettings::get_singleton()->notify_changes();
66 	EditorSettings::get_singleton()->save();
67 }
68 
cancel_pressed()69 void EditorSettingsDialog::cancel_pressed() {
70 
71 	if (!EditorSettings::get_singleton())
72 		return;
73 
74 	EditorSettings::get_singleton()->notify_changes();
75 }
76 
popup_edit_settings()77 void EditorSettingsDialog::popup_edit_settings() {
78 
79 	if (!EditorSettings::get_singleton())
80 		return;
81 
82 	EditorSettings::get_singleton()->list_text_editor_themes(); // make sure we have an up to date list of themes
83 
84 	property_editor->edit(EditorSettings::get_singleton());
85 	property_editor->get_property_editor()->update_tree();
86 
87 	search_box->select_all();
88 	search_box->grab_focus();
89 
90 	_update_shortcuts();
91 	popup_centered_ratio(0.7);
92 }
93 
_clear_search_box()94 void EditorSettingsDialog::_clear_search_box() {
95 
96 	if (search_box->get_text() == "")
97 		return;
98 
99 	search_box->clear();
100 	property_editor->get_property_editor()->update_tree();
101 }
102 
_clear_shortcut_search_box()103 void EditorSettingsDialog::_clear_shortcut_search_box() {
104 	if (shortcut_search_box->get_text() == "")
105 		return;
106 
107 	shortcut_search_box->clear();
108 }
109 
_filter_shortcuts(const String & p_filter)110 void EditorSettingsDialog::_filter_shortcuts(const String &p_filter) {
111 	shortcut_filter = p_filter;
112 	_update_shortcuts();
113 }
114 
_notification(int p_what)115 void EditorSettingsDialog::_notification(int p_what) {
116 
117 	if (p_what == NOTIFICATION_ENTER_TREE) {
118 
119 		clear_button->set_icon(get_icon("Close", "EditorIcons"));
120 		shortcut_clear_button->set_icon(get_icon("Close", "EditorIcons"));
121 	}
122 }
123 
_update_shortcuts()124 void EditorSettingsDialog::_update_shortcuts() {
125 
126 	shortcuts->clear();
127 
128 	List<String> slist;
129 	EditorSettings::get_singleton()->get_shortcut_list(&slist);
130 	TreeItem *root = shortcuts->create_item();
131 
132 	Map<String, TreeItem *> sections;
133 
134 	for (List<String>::Element *E = slist.front(); E; E = E->next()) {
135 
136 		Ref<ShortCut> sc = EditorSettings::get_singleton()->get_shortcut(E->get());
137 		if (!sc->has_meta("original"))
138 			continue;
139 
140 		InputEvent original = sc->get_meta("original");
141 
142 		String section_name = E->get().get_slice("/", 0);
143 
144 		TreeItem *section;
145 
146 		if (sections.has(section_name)) {
147 			section = sections[section_name];
148 		} else {
149 			section = shortcuts->create_item(root);
150 			section->set_text(0, section_name.capitalize());
151 
152 			sections[section_name] = section;
153 			section->set_custom_bg_color(0, get_color("prop_subsection", "Editor"));
154 			section->set_custom_bg_color(1, get_color("prop_subsection", "Editor"));
155 		}
156 
157 		if (shortcut_filter.is_subsequence_ofi(sc->get_name())) {
158 			TreeItem *item = shortcuts->create_item(section);
159 
160 			item->set_text(0, sc->get_name());
161 			item->set_text(1, sc->get_as_text());
162 			if (!sc->is_shortcut(original) && !(sc->get_shortcut().type == InputEvent::NONE && original.type == InputEvent::NONE)) {
163 				item->add_button(1, get_icon("Reload", "EditorIcons"), 2);
164 			}
165 			item->add_button(1, get_icon("Edit", "EditorIcons"), 0);
166 			item->add_button(1, get_icon("Close", "EditorIcons"), 1);
167 			item->set_tooltip(0, E->get());
168 			item->set_metadata(0, E->get());
169 		}
170 	}
171 
172 	// remove sections with no shortcuts
173 	for (Map<String, TreeItem *>::Element *E = sections.front(); E; E = E->next()) {
174 		TreeItem *section = E->get();
175 		if (section->get_children() == NULL) {
176 			root->remove_child(section);
177 		}
178 	}
179 }
180 
_shortcut_button_pressed(Object * p_item,int p_column,int p_idx)181 void EditorSettingsDialog::_shortcut_button_pressed(Object *p_item, int p_column, int p_idx) {
182 
183 	TreeItem *ti = p_item->cast_to<TreeItem>();
184 	ERR_FAIL_COND(!ti);
185 
186 	String item = ti->get_metadata(0);
187 	Ref<ShortCut> sc = EditorSettings::get_singleton()->get_shortcut(item);
188 
189 	if (p_idx == 0) {
190 		press_a_key_label->set_text(TTR("Press a Key.."));
191 		last_wait_for_key = InputEvent();
192 		press_a_key->popup_centered(Size2(250, 80) * EDSCALE);
193 		press_a_key->grab_focus();
194 		press_a_key->get_ok()->set_focus_mode(FOCUS_NONE);
195 		press_a_key->get_cancel()->set_focus_mode(FOCUS_NONE);
196 		shortcut_configured = item;
197 
198 	} else if (p_idx == 1) { //erase
199 		if (!sc.is_valid())
200 			return; //pointless, there is nothing
201 
202 		UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
203 		ur->create_action("Erase Shortcut");
204 		ur->add_do_method(sc.ptr(), "set_shortcut", InputEvent());
205 		ur->add_undo_method(sc.ptr(), "set_shortcut", sc->get_shortcut());
206 		ur->add_do_method(this, "_update_shortcuts");
207 		ur->add_undo_method(this, "_update_shortcuts");
208 		ur->add_do_method(this, "_settings_changed");
209 		ur->add_undo_method(this, "_settings_changed");
210 		ur->commit_action();
211 	} else if (p_idx == 2) { //revert to original
212 		if (!sc.is_valid())
213 			return; //pointless, there is nothing
214 
215 		InputEvent original = sc->get_meta("original");
216 
217 		UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
218 		ur->create_action("Restore Shortcut");
219 		ur->add_do_method(sc.ptr(), "set_shortcut", original);
220 		ur->add_undo_method(sc.ptr(), "set_shortcut", sc->get_shortcut());
221 		ur->add_do_method(this, "_update_shortcuts");
222 		ur->add_undo_method(this, "_update_shortcuts");
223 		ur->add_do_method(this, "_settings_changed");
224 		ur->add_undo_method(this, "_settings_changed");
225 		ur->commit_action();
226 	}
227 }
228 
_wait_for_key(const InputEvent & p_event)229 void EditorSettingsDialog::_wait_for_key(const InputEvent &p_event) {
230 
231 	if (p_event.type == InputEvent::KEY && p_event.key.pressed && p_event.key.scancode != 0) {
232 
233 		last_wait_for_key = p_event;
234 		String str = keycode_get_string(p_event.key.scancode).capitalize();
235 		if (p_event.key.mod.meta)
236 			str = TTR("Meta+") + str;
237 		if (p_event.key.mod.shift)
238 			str = TTR("Shift+") + str;
239 		if (p_event.key.mod.alt)
240 			str = TTR("Alt+") + str;
241 		if (p_event.key.mod.control)
242 			str = TTR("Control+") + str;
243 
244 		press_a_key_label->set_text(str);
245 		press_a_key->accept_event();
246 	}
247 }
248 
_press_a_key_confirm()249 void EditorSettingsDialog::_press_a_key_confirm() {
250 
251 	if (last_wait_for_key.type != InputEvent::KEY)
252 		return;
253 
254 	InputEvent ie;
255 	ie.type = InputEvent::KEY;
256 	ie.key.scancode = last_wait_for_key.key.scancode;
257 	ie.key.mod = last_wait_for_key.key.mod;
258 
259 	Ref<ShortCut> sc = EditorSettings::get_singleton()->get_shortcut(shortcut_configured);
260 
261 	UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
262 	ur->create_action("Change Shortcut '" + shortcut_configured + "'");
263 	ur->add_do_method(sc.ptr(), "set_shortcut", ie);
264 	ur->add_undo_method(sc.ptr(), "set_shortcut", sc->get_shortcut());
265 	ur->add_do_method(this, "_update_shortcuts");
266 	ur->add_undo_method(this, "_update_shortcuts");
267 	ur->add_do_method(this, "_settings_changed");
268 	ur->add_undo_method(this, "_settings_changed");
269 	ur->commit_action();
270 }
271 
_bind_methods()272 void EditorSettingsDialog::_bind_methods() {
273 
274 	ObjectTypeDB::bind_method(_MD("_settings_save"), &EditorSettingsDialog::_settings_save);
275 	ObjectTypeDB::bind_method(_MD("_settings_changed"), &EditorSettingsDialog::_settings_changed);
276 	ObjectTypeDB::bind_method(_MD("_settings_property_edited"), &EditorSettingsDialog::_settings_property_edited);
277 	ObjectTypeDB::bind_method(_MD("_clear_search_box"), &EditorSettingsDialog::_clear_search_box);
278 	ObjectTypeDB::bind_method(_MD("_clear_shortcut_search_box"), &EditorSettingsDialog::_clear_shortcut_search_box);
279 	ObjectTypeDB::bind_method(_MD("_shortcut_button_pressed"), &EditorSettingsDialog::_shortcut_button_pressed);
280 	ObjectTypeDB::bind_method(_MD("_filter_shortcuts"), &EditorSettingsDialog::_filter_shortcuts);
281 	ObjectTypeDB::bind_method(_MD("_update_shortcuts"), &EditorSettingsDialog::_update_shortcuts);
282 	ObjectTypeDB::bind_method(_MD("_press_a_key_confirm"), &EditorSettingsDialog::_press_a_key_confirm);
283 	ObjectTypeDB::bind_method(_MD("_wait_for_key"), &EditorSettingsDialog::_wait_for_key);
284 }
285 
EditorSettingsDialog()286 EditorSettingsDialog::EditorSettingsDialog() {
287 
288 	set_title(TTR("Editor Settings"));
289 
290 	tabs = memnew(TabContainer);
291 	add_child(tabs);
292 	set_child_rect(tabs);
293 
294 	VBoxContainer *vbc = memnew(VBoxContainer);
295 	tabs->add_child(vbc);
296 	vbc->set_name(TTR("General"));
297 
298 	HBoxContainer *hbc = memnew(HBoxContainer);
299 	hbc->set_h_size_flags(Control::SIZE_EXPAND_FILL);
300 	vbc->add_child(hbc);
301 
302 	Label *l = memnew(Label);
303 	l->set_text(TTR("Search:") + " ");
304 	hbc->add_child(l);
305 
306 	search_box = memnew(LineEdit);
307 	search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL);
308 	hbc->add_child(search_box);
309 
310 	clear_button = memnew(ToolButton);
311 	hbc->add_child(clear_button);
312 	clear_button->connect("pressed", this, "_clear_search_box");
313 
314 	property_editor = memnew(SectionedPropertyEditor);
315 	//property_editor->hide_top_label();
316 	property_editor->get_property_editor()->set_use_filter(true);
317 	property_editor->get_property_editor()->register_text_enter(search_box);
318 	property_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
319 	vbc->add_child(property_editor);
320 	property_editor->get_property_editor()->connect("property_edited", this, "_settings_property_edited");
321 
322 	vbc = memnew(VBoxContainer);
323 	tabs->add_child(vbc);
324 	vbc->set_name(TTR("Shortcuts"));
325 
326 	hbc = memnew(HBoxContainer);
327 	hbc->set_h_size_flags(Control::SIZE_EXPAND_FILL);
328 	vbc->add_child(hbc);
329 
330 	l = memnew(Label);
331 	l->set_text(TTR("Search:") + " ");
332 	hbc->add_child(l);
333 
334 	shortcut_search_box = memnew(LineEdit);
335 	shortcut_search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL);
336 	hbc->add_child(shortcut_search_box);
337 	shortcut_search_box->connect("text_changed", this, "_filter_shortcuts");
338 
339 	shortcut_clear_button = memnew(ToolButton);
340 	hbc->add_child(shortcut_clear_button);
341 	shortcut_clear_button->connect("pressed", this, "_clear_shortcut_search_box");
342 
343 	shortcuts = memnew(Tree);
344 	vbc->add_margin_child("Shortcut List:", shortcuts, true);
345 	shortcuts->set_columns(2);
346 	shortcuts->set_hide_root(true);
347 	//shortcuts->set_hide_folding(true);
348 	shortcuts->set_column_titles_visible(true);
349 	shortcuts->set_column_title(0, "Name");
350 	shortcuts->set_column_title(1, "Binding");
351 	shortcuts->connect("button_pressed", this, "_shortcut_button_pressed");
352 
353 	press_a_key = memnew(ConfirmationDialog);
354 	press_a_key->set_focus_mode(FOCUS_ALL);
355 	add_child(press_a_key);
356 
357 	l = memnew(Label);
358 	l->set_text(TTR("Press a Key.."));
359 	l->set_area_as_parent_rect();
360 	l->set_align(Label::ALIGN_CENTER);
361 	l->set_margin(MARGIN_TOP, 20);
362 	l->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_BEGIN, 30);
363 	press_a_key_label = l;
364 	press_a_key->add_child(l);
365 	press_a_key->connect("input_event", this, "_wait_for_key");
366 	press_a_key->connect("confirmed", this, "_press_a_key_confirm");
367 
368 	//get_ok()->set_text("Apply");
369 	set_hide_on_ok(true);
370 	//get_cancel()->set_text("Close");
371 
372 	timer = memnew(Timer);
373 	timer->set_wait_time(1.5);
374 	timer->connect("timeout", this, "_settings_save");
375 	timer->set_one_shot(true);
376 	add_child(timer);
377 	EditorSettings::get_singleton()->connect("settings_changed", this, "_settings_changed");
378 	get_ok()->set_text(TTR("Close"));
379 
380 	updating = false;
381 }
382