3535#include " editor/docks/filesystem_dock.h"
3636#include " editor/editor_node.h"
3737#include " editor/editor_string_names.h"
38+ #include " editor/editor_undo_redo_manager.h"
3839#include " editor/file_system/editor_file_system.h"
3940#include " editor/file_system/editor_paths.h"
4041#include " editor/inspector/editor_resource_preview.h"
42+ #include " editor/inspector/multi_node_edit.h"
4143#include " editor/settings/editor_settings.h"
4244#include " editor/themes/editor_scale.h"
4345#include " scene/gui/center_container.h"
@@ -123,7 +125,8 @@ EditorQuickOpenDialog::EditorQuickOpenDialog() {
123125
124126 {
125127 container = memnew (QuickOpenResultContainer);
126- container->connect (" result_clicked" , callable_mp (this , &EditorQuickOpenDialog::ok_pressed));
128+ container->connect (" selection_changed" , callable_mp (this , &EditorQuickOpenDialog::selection_changed));
129+ container->connect (" result_clicked" , callable_mp (this , &EditorQuickOpenDialog::item_pressed));
127130 vbc->add_child (container);
128131 }
129132
@@ -149,26 +152,117 @@ void EditorQuickOpenDialog::popup_dialog(const Vector<StringName> &p_base_types,
149152 ERR_FAIL_COND (p_base_types.is_empty ());
150153 ERR_FAIL_COND (!p_item_selected_callback.is_valid ());
151154
155+ property_object = nullptr ;
156+ property_path = " " ;
152157 item_selected_callback = p_item_selected_callback;
153158
154159 container->init (p_base_types);
155- get_ok_button ()->set_disabled (container->has_nothing_selected ());
160+ container->set_instant_preview_toggle_visible (false );
161+ _finish_dialog_setup (p_base_types);
162+ }
163+
164+ void EditorQuickOpenDialog::popup_dialog_for_property (const Vector<StringName> &p_base_types, Object *p_obj, const StringName &p_path, const Callable &p_item_selected_callback) {
165+ ERR_FAIL_NULL (p_obj);
166+ ERR_FAIL_COND (p_base_types.is_empty ());
167+ ERR_FAIL_COND (!p_item_selected_callback.is_valid ());
168+
169+ property_object = p_obj;
170+ property_path = p_path;
171+ item_selected_callback = p_item_selected_callback;
172+ initial_property_value = property_object->get (property_path);
156173
174+ // Reset this, so that the property isn't updated immediately upon opening
175+ // the window.
176+ initial_selection_performed = false ;
177+
178+ container->init (p_base_types);
179+ container->set_instant_preview_toggle_visible (true );
180+ _finish_dialog_setup (p_base_types);
181+ }
182+
183+ void EditorQuickOpenDialog::_finish_dialog_setup (const Vector<StringName> &p_base_types) {
184+ get_ok_button ()->set_disabled (container->has_nothing_selected ());
157185 set_title (get_dialog_title (p_base_types));
158186 popup_centered_clamped (Size2 (780 , 650 ) * EDSCALE, 0 .8f );
159187 search_box->grab_focus ();
160188}
161189
162190void EditorQuickOpenDialog::ok_pressed () {
163- item_selected_callback.call (container->get_selected ());
164-
165- container->save_selected_item ();
191+ update_property ();
166192 container->cleanup ();
167193 search_box->clear ();
168194 hide ();
169195}
170196
197+ bool EditorQuickOpenDialog::_is_instant_preview_active () const {
198+ return property_object != nullptr && container->is_instant_preview_enabled ();
199+ }
200+
201+ void EditorQuickOpenDialog::selection_changed () {
202+ if (!_is_instant_preview_active ()) {
203+ return ;
204+ }
205+
206+ // This prevents the property from being changed the first time the Quick Open
207+ // window is opened.
208+ if (!initial_selection_performed) {
209+ initial_selection_performed = true ;
210+ } else {
211+ preview_property ();
212+ }
213+ }
214+
215+ void EditorQuickOpenDialog::item_pressed (bool p_double_click) {
216+ // A double-click should always be taken as a "confirm" action.
217+ if (p_double_click) {
218+ container->save_selected_item ();
219+ ok_pressed ();
220+ return ;
221+ }
222+
223+ // Single-clicks should be taken as a "confirm" action only if Instant Preview
224+ // isn't currently enabled, or the property object is null for some reason.
225+ if (!_is_instant_preview_active ()) {
226+ container->save_selected_item ();
227+ ok_pressed ();
228+ }
229+ }
230+
231+ void EditorQuickOpenDialog::preview_property () {
232+ Ref<Resource> loaded_resource = ResourceLoader::load (container->get_selected ());
233+ ERR_FAIL_COND_MSG (loaded_resource.is_null (), " Cannot load resource from path '" + container->get_selected () + " '." );
234+
235+ // MultiNodeEdit has adding to the undo/redo stack baked into its set function.
236+ // As such, we have to specifically call a version of its setter that doesn't
237+ // create undo/redo actions.
238+ if (Object::cast_to<MultiNodeEdit>(property_object)) {
239+ Object::cast_to<MultiNodeEdit>(property_object)->_set_impl (property_path, loaded_resource, " " , false );
240+ } else {
241+ property_object->set (property_path, loaded_resource);
242+ }
243+ }
244+
245+ void EditorQuickOpenDialog::update_property () {
246+ // Set the property back to the initial value first, so that the undo action
247+ // has the correct object.
248+ if (property_object) {
249+ if (Object::cast_to<MultiNodeEdit>(property_object)) {
250+ Object::cast_to<MultiNodeEdit>(property_object)->_set_impl (property_path, initial_property_value, " " , false );
251+ } else {
252+ property_object->set (property_path, initial_property_value);
253+ }
254+ }
255+ item_selected_callback.call (container->get_selected ());
256+ }
257+
171258void EditorQuickOpenDialog::cancel_pressed () {
259+ if (property_object) {
260+ if (Object::cast_to<MultiNodeEdit>(property_object)) {
261+ Object::cast_to<MultiNodeEdit>(property_object)->_set_impl (property_path, initial_property_value, " " , false );
262+ } else {
263+ property_object->set (property_path, initial_property_value);
264+ }
265+ }
172266 container->cleanup ();
173267 search_box->clear ();
174268}
@@ -262,6 +356,13 @@ QuickOpenResultContainer::QuickOpenResultContainer() {
262356 bottom_bar->add_theme_constant_override (" separation" , 3 );
263357 add_child (bottom_bar);
264358
359+ instant_preview_toggle = memnew (CheckButton);
360+ style_button (instant_preview_toggle);
361+ instant_preview_toggle->set_text (TTRC (" Instant Preview" ));
362+ instant_preview_toggle->set_tooltip_text (TTRC (" Selected resource will be previewed in the editor before accepting." ));
363+ instant_preview_toggle->connect (SceneStringName (toggled), callable_mp (this , &QuickOpenResultContainer::_toggle_instant_preview));
364+ bottom_bar->add_child (instant_preview_toggle);
365+
265366 fuzzy_search_toggle = memnew (CheckButton);
266367 style_button (fuzzy_search_toggle);
267368 fuzzy_search_toggle->set_text (TTR (" Fuzzy Search" ));
@@ -333,8 +434,10 @@ void QuickOpenResultContainer::init(const Vector<StringName> &p_base_types) {
333434 _set_display_mode ((QuickOpenDisplayMode)last);
334435 }
335436
437+ const bool do_instant_preview = EDITOR_GET (" filesystem/quick_open_dialog/instant_preview" );
336438 const bool fuzzy_matching = EDITOR_GET (" filesystem/quick_open_dialog/enable_fuzzy_matching" );
337439 const bool include_addons = EDITOR_GET (" filesystem/quick_open_dialog/include_addons" );
440+ instant_preview_toggle->set_pressed_no_signal (do_instant_preview);
338441 fuzzy_search_toggle->set_pressed_no_signal (fuzzy_matching);
339442 include_addons_toggle->set_pressed_no_signal (include_addons);
340443 never_opened = false ;
@@ -689,6 +792,8 @@ void QuickOpenResultContainer::_select_item(int p_index) {
689792 bool in_history = history_set.has (candidates[selection_index].file_path );
690793 file_details_path->set_text (get_selected () + (in_history ? TTR (" (recently opened)" ) : " " ));
691794
795+ emit_signal (SNAME (" selection_changed" ));
796+
692797 const QuickOpenResultItem *item = result_items[selection_index];
693798
694799 // Copied from Tree.
@@ -710,7 +815,7 @@ void QuickOpenResultContainer::_item_input(const Ref<InputEvent> &p_ev, int p_in
710815 if (mb.is_valid () && mb->is_pressed ()) {
711816 if (mb->get_button_index () == MouseButton::LEFT) {
712817 _select_item (p_index);
713- emit_signal (SNAME (" result_clicked" ));
818+ emit_signal (SNAME (" result_clicked" ), mb-> is_double_click () );
714819 } else if (mb->get_button_index () == MouseButton::RIGHT) {
715820 _select_item (p_index);
716821 file_context_menu->set_position (result_items[p_index]->get_screen_position () + mb->get_position ());
@@ -720,6 +825,10 @@ void QuickOpenResultContainer::_item_input(const Ref<InputEvent> &p_ev, int p_in
720825 }
721826}
722827
828+ void QuickOpenResultContainer::_toggle_instant_preview (bool p_pressed) {
829+ EditorSettings::get_singleton ()->set (" filesystem/quick_open_dialog/instant_preview" , p_pressed);
830+ }
831+
723832void QuickOpenResultContainer::_toggle_fuzzy_search (bool p_pressed) {
724833 EditorSettings::get_singleton ()->set (" filesystem/quick_open_dialog/enable_fuzzy_matching" , p_pressed);
725834 update_results ();
@@ -820,6 +929,14 @@ String _get_uid_string(const String &p_filepath) {
820929 return id == ResourceUID::INVALID_ID ? p_filepath : ResourceUID::get_singleton ()->id_to_text (id);
821930}
822931
932+ bool QuickOpenResultContainer::is_instant_preview_enabled () const {
933+ return instant_preview_toggle && instant_preview_toggle->is_visible () && instant_preview_toggle->is_pressed ();
934+ }
935+
936+ void QuickOpenResultContainer::set_instant_preview_toggle_visible (bool p_visible) {
937+ instant_preview_toggle->set_visible (p_visible);
938+ }
939+
823940void QuickOpenResultContainer::save_selected_item () {
824941 if (base_types.size () > 1 ) {
825942 // Getting the type of the file and checking which base type it belongs to should be possible.
@@ -895,13 +1012,14 @@ void QuickOpenResultContainer::_notification(int p_what) {
8951012}
8961013
8971014void QuickOpenResultContainer::_bind_methods () {
898- ADD_SIGNAL (MethodInfo (" result_clicked" ));
1015+ ADD_SIGNAL (MethodInfo (" selection_changed" ));
1016+ ADD_SIGNAL (MethodInfo (" result_clicked" , PropertyInfo (Variant::BOOL, " double_click" )));
8991017}
9001018
9011019// ------------------------- Result Item
9021020
9031021QuickOpenResultItem::QuickOpenResultItem () {
904- set_focus_mode (FocusMode::FOCUS_ALL );
1022+ set_focus_mode (FocusMode::FOCUS_NONE );
9051023 _set_enabled (false );
9061024 set_default_cursor_shape (CURSOR_POINTING_HAND);
9071025
0 commit comments