2023-12-02 21:33:38 +01:00
/**************************************************************************/
/* editor_quick_open_dialog.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
# include "editor_quick_open_dialog.h"
2024-12-21 01:43:50 +01:00
# include "core/config/project_settings.h"
2024-10-10 23:50:50 -07:00
# include "core/string/fuzzy_search.h"
2023-12-02 21:33:38 +01:00
# include "editor/editor_file_system.h"
# include "editor/editor_node.h"
2024-12-11 15:11:47 +01:00
# include "editor/editor_paths.h"
2023-12-02 21:33:38 +01:00
# include "editor/editor_resource_preview.h"
# include "editor/editor_settings.h"
# include "editor/editor_string_names.h"
2024-12-21 01:43:50 +01:00
# include "editor/filesystem_dock.h"
2023-12-02 21:33:38 +01:00
# include "editor/themes/editor_scale.h"
# include "scene/gui/center_container.h"
# include "scene/gui/check_button.h"
# include "scene/gui/flow_container.h"
2025-05-15 14:28:00 +08:00
# include "scene/gui/line_edit.h"
2023-12-02 21:33:38 +01:00
# include "scene/gui/margin_container.h"
# include "scene/gui/panel_container.h"
# include "scene/gui/separator.h"
# include "scene/gui/texture_rect.h"
# include "scene/gui/tree.h"
2024-10-10 23:50:50 -07:00
void HighlightedLabel : : draw_substr_rects ( const Vector2i & p_substr , Vector2 p_offset , int p_line_limit , int line_spacing ) {
for ( int i = get_lines_skipped ( ) ; i < p_line_limit ; i + + ) {
RID line = get_line_rid ( i ) ;
Vector < Vector2 > ranges = TS - > shaped_text_get_selection ( line , p_substr . x , p_substr . x + p_substr . y ) ;
Rect2 line_rect = get_line_rect ( i ) ;
for ( const Vector2 & range : ranges ) {
Rect2 rect = Rect2 ( Point2 ( range . x , 0 ) + line_rect . position , Size2 ( range . y - range . x , line_rect . size . y ) ) ;
rect . position = p_offset + line_rect . position ;
rect . position . x + = range . x ;
rect . size = Size2 ( range . y - range . x , line_rect . size . y ) ;
rect . size . x = MIN ( rect . size . x , line_rect . size . x - range . x ) ;
if ( rect . size . x > 0 ) {
draw_rect ( rect , Color ( 1 , 1 , 1 , 0.07 ) , true ) ;
draw_rect ( rect , Color ( 0.5 , 0.7 , 1.0 , 0.4 ) , false , 1 ) ;
}
}
p_offset . y + = line_spacing + TS - > shaped_text_get_ascent ( line ) + TS - > shaped_text_get_descent ( line ) ;
}
}
void HighlightedLabel : : add_highlight ( const Vector2i & p_interval ) {
if ( p_interval . y > 0 ) {
highlights . append ( p_interval ) ;
queue_redraw ( ) ;
}
}
void HighlightedLabel : : reset_highlights ( ) {
highlights . clear ( ) ;
queue_redraw ( ) ;
}
void HighlightedLabel : : _notification ( int p_notification ) {
if ( p_notification = = NOTIFICATION_DRAW ) {
if ( highlights . is_empty ( ) ) {
return ;
}
Vector2 offset ;
int line_limit ;
int line_spacing ;
get_layout_data ( offset , line_limit , line_spacing ) ;
for ( const Vector2i & substr : highlights ) {
draw_substr_rects ( substr , offset , line_limit , line_spacing ) ;
}
}
}
2023-12-02 21:33:38 +01:00
EditorQuickOpenDialog : : EditorQuickOpenDialog ( ) {
VBoxContainer * vbc = memnew ( VBoxContainer ) ;
vbc - > add_theme_constant_override ( " separation " , 0 ) ;
add_child ( vbc ) ;
{
// Search bar
MarginContainer * mc = memnew ( MarginContainer ) ;
mc - > add_theme_constant_override ( " margin_top " , 6 ) ;
mc - > add_theme_constant_override ( " margin_bottom " , 6 ) ;
mc - > add_theme_constant_override ( " margin_left " , 1 ) ;
mc - > add_theme_constant_override ( " margin_right " , 1 ) ;
vbc - > add_child ( mc ) ;
search_box = memnew ( LineEdit ) ;
search_box - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
search_box - > set_placeholder ( TTR ( " Search files... " ) ) ;
2025-03-21 09:55:22 +02:00
search_box - > set_accessibility_name ( TTRC ( " Search " ) ) ;
2023-12-02 21:33:38 +01:00
search_box - > set_clear_button_enabled ( true ) ;
mc - > add_child ( search_box ) ;
}
{
container = memnew ( QuickOpenResultContainer ) ;
container - > connect ( " result_clicked " , callable_mp ( this , & EditorQuickOpenDialog : : ok_pressed ) ) ;
vbc - > add_child ( container ) ;
}
search_box - > connect ( SceneStringName ( text_changed ) , callable_mp ( this , & EditorQuickOpenDialog : : _search_box_text_changed ) ) ;
search_box - > connect ( SceneStringName ( gui_input ) , callable_mp ( container , & QuickOpenResultContainer : : handle_search_box_input ) ) ;
register_text_enter ( search_box ) ;
get_ok_button ( ) - > hide ( ) ;
}
String EditorQuickOpenDialog : : get_dialog_title ( const Vector < StringName > & p_base_types ) {
if ( p_base_types . size ( ) > 1 ) {
return TTR ( " Select Resource " ) ;
}
if ( p_base_types [ 0 ] = = SNAME ( " PackedScene " ) ) {
return TTR ( " Select Scene " ) ;
}
return TTR ( " Select " ) + " " + p_base_types [ 0 ] ;
}
void EditorQuickOpenDialog : : popup_dialog ( const Vector < StringName > & p_base_types , const Callable & p_item_selected_callback ) {
ERR_FAIL_COND ( p_base_types . is_empty ( ) ) ;
ERR_FAIL_COND ( ! p_item_selected_callback . is_valid ( ) ) ;
item_selected_callback = p_item_selected_callback ;
container - > init ( p_base_types ) ;
get_ok_button ( ) - > set_disabled ( container - > has_nothing_selected ( ) ) ;
set_title ( get_dialog_title ( p_base_types ) ) ;
2024-10-10 23:50:50 -07:00
popup_centered_clamped ( Size2 ( 780 , 650 ) * EDSCALE , 0.8f ) ;
2023-12-02 21:33:38 +01:00
search_box - > grab_focus ( ) ;
}
void EditorQuickOpenDialog : : ok_pressed ( ) {
item_selected_callback . call ( container - > get_selected ( ) ) ;
container - > save_selected_item ( ) ;
container - > cleanup ( ) ;
search_box - > clear ( ) ;
hide ( ) ;
}
void EditorQuickOpenDialog : : cancel_pressed ( ) {
container - > cleanup ( ) ;
search_box - > clear ( ) ;
}
void EditorQuickOpenDialog : : _search_box_text_changed ( const String & p_query ) {
2024-10-10 23:50:50 -07:00
container - > set_query_and_update ( p_query ) ;
2023-12-02 21:33:38 +01:00
get_ok_button ( ) - > set_disabled ( container - > has_nothing_selected ( ) ) ;
}
//------------------------- Result Container
2024-10-10 23:50:50 -07:00
void style_button ( Button * p_button ) {
p_button - > set_flat ( true ) ;
p_button - > set_focus_mode ( Control : : FOCUS_NONE ) ;
p_button - > set_default_cursor_shape ( Control : : CURSOR_POINTING_HAND ) ;
}
2023-12-02 21:33:38 +01:00
QuickOpenResultContainer : : QuickOpenResultContainer ( ) {
set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
set_v_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
add_theme_constant_override ( " separation " , 0 ) ;
2024-12-11 15:11:47 +01:00
history_file . instantiate ( ) ;
2023-12-02 21:33:38 +01:00
{
// Results section
panel_container = memnew ( PanelContainer ) ;
panel_container - > set_v_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
add_child ( panel_container ) ;
{
// No search results
no_results_container = memnew ( CenterContainer ) ;
no_results_container - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
no_results_container - > set_v_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
panel_container - > add_child ( no_results_container ) ;
no_results_label = memnew ( Label ) ;
2025-04-22 11:57:16 +03:00
no_results_label - > set_focus_mode ( FOCUS_ACCESSIBILITY ) ;
2023-12-02 21:33:38 +01:00
no_results_label - > add_theme_font_size_override ( SceneStringName ( font_size ) , 24 * EDSCALE ) ;
no_results_container - > add_child ( no_results_label ) ;
no_results_container - > hide ( ) ;
}
{
// Search results
scroll_container = memnew ( ScrollContainer ) ;
scroll_container - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
scroll_container - > set_v_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
scroll_container - > set_horizontal_scroll_mode ( ScrollContainer : : SCROLL_MODE_DISABLED ) ;
scroll_container - > hide ( ) ;
panel_container - > add_child ( scroll_container ) ;
list = memnew ( VBoxContainer ) ;
list - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
2025-01-15 15:36:54 +02:00
list - > add_theme_constant_override ( SNAME ( " separation " ) , 0 ) ;
2023-12-02 21:33:38 +01:00
list - > hide ( ) ;
scroll_container - > add_child ( list ) ;
grid = memnew ( HFlowContainer ) ;
grid - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
grid - > set_v_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
2025-01-15 15:36:54 +02:00
grid - > add_theme_constant_override ( SNAME ( " v_separation " ) , 0 ) ;
grid - > add_theme_constant_override ( SNAME ( " h_separation " ) , 0 ) ;
2023-12-02 21:33:38 +01:00
grid - > hide ( ) ;
scroll_container - > add_child ( grid ) ;
2024-12-21 01:43:50 +01:00
file_context_menu = memnew ( PopupMenu ) ;
file_context_menu - > add_item ( TTR ( " Show in FileSystem " ) , FILE_SHOW_IN_FILESYSTEM ) ;
file_context_menu - > add_item ( TTR ( " Show in File Manager " ) , FILE_SHOW_IN_FILE_MANAGER ) ;
file_context_menu - > connect ( SceneStringName ( id_pressed ) , callable_mp ( this , & QuickOpenResultContainer : : _menu_option ) ) ;
file_context_menu - > hide ( ) ;
scroll_container - > add_child ( file_context_menu ) ;
2023-12-02 21:33:38 +01:00
}
}
{
2024-10-10 23:50:50 -07:00
// Selected filepath
2023-12-02 21:33:38 +01:00
file_details_path = memnew ( Label ) ;
2025-04-22 11:57:16 +03:00
file_details_path - > set_focus_mode ( FOCUS_ACCESSIBILITY ) ;
2023-12-02 21:33:38 +01:00
file_details_path - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
file_details_path - > set_horizontal_alignment ( HorizontalAlignment : : HORIZONTAL_ALIGNMENT_CENTER ) ;
file_details_path - > set_text_overrun_behavior ( TextServer : : OVERRUN_TRIM_ELLIPSIS ) ;
2024-10-10 23:50:50 -07:00
add_child ( file_details_path ) ;
}
2023-12-02 21:33:38 +01:00
2024-10-10 23:50:50 -07:00
{
// Bottom bar
HBoxContainer * bottom_bar = memnew ( HBoxContainer ) ;
bottom_bar - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
bottom_bar - > set_alignment ( ALIGNMENT_END ) ;
bottom_bar - > add_theme_constant_override ( " separation " , 3 ) ;
add_child ( bottom_bar ) ;
fuzzy_search_toggle = memnew ( CheckButton ) ;
style_button ( fuzzy_search_toggle ) ;
fuzzy_search_toggle - > set_text ( TTR ( " Fuzzy Search " ) ) ;
2025-06-03 22:43:21 -07:00
fuzzy_search_toggle - > set_tooltip_text ( TTRC ( " Include inexact matches or matches starting in the middle of a filename, instead of only exact matches from the beginning. " ) ) ;
2024-10-10 23:50:50 -07:00
fuzzy_search_toggle - > connect ( SceneStringName ( toggled ) , callable_mp ( this , & QuickOpenResultContainer : : _toggle_fuzzy_search ) ) ;
bottom_bar - > add_child ( fuzzy_search_toggle ) ;
include_addons_toggle = memnew ( CheckButton ) ;
style_button ( include_addons_toggle ) ;
include_addons_toggle - > set_text ( TTR ( " Addons " ) ) ;
include_addons_toggle - > set_tooltip_text ( TTR ( " Include files from addons " ) ) ;
include_addons_toggle - > connect ( SceneStringName ( toggled ) , callable_mp ( this , & QuickOpenResultContainer : : _toggle_include_addons ) ) ;
bottom_bar - > add_child ( include_addons_toggle ) ;
VSeparator * vsep = memnew ( VSeparator ) ;
vsep - > set_v_size_flags ( Control : : SIZE_SHRINK_CENTER ) ;
vsep - > set_custom_minimum_size ( Size2i ( 0 , 14 * EDSCALE ) ) ;
bottom_bar - > add_child ( vsep ) ;
display_mode_toggle = memnew ( Button ) ;
2025-03-21 09:55:22 +02:00
display_mode_toggle - > set_accessibility_name ( TTRC ( " Display Mode " ) ) ;
2024-10-10 23:50:50 -07:00
style_button ( display_mode_toggle ) ;
display_mode_toggle - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & QuickOpenResultContainer : : _toggle_display_mode ) ) ;
bottom_bar - > add_child ( display_mode_toggle ) ;
2023-12-02 21:33:38 +01:00
}
2024-10-10 23:50:50 -07:00
}
2023-12-02 21:33:38 +01:00
2024-12-21 01:43:50 +01:00
void QuickOpenResultContainer : : _menu_option ( int p_option ) {
switch ( p_option ) {
case FILE_SHOW_IN_FILESYSTEM : {
FileSystemDock : : get_singleton ( ) - > navigate_to_path ( get_selected ( ) ) ;
} break ;
case FILE_SHOW_IN_FILE_MANAGER : {
String dir = ProjectSettings : : get_singleton ( ) - > globalize_path ( get_selected ( ) ) ;
OS : : get_singleton ( ) - > shell_show_in_file_manager ( dir , true ) ;
} break ;
}
}
2024-10-10 23:50:50 -07:00
void QuickOpenResultContainer : : _ensure_result_vector_capacity ( ) {
int target_size = EDITOR_GET ( " filesystem/quick_open_dialog/max_results " ) ;
int initial_size = result_items . size ( ) ;
for ( int i = target_size ; i < initial_size ; i + + ) {
result_items [ i ] - > queue_free ( ) ;
}
result_items . resize ( target_size ) ;
for ( int i = initial_size ; i < target_size ; i + + ) {
2023-12-02 21:33:38 +01:00
QuickOpenResultItem * item = memnew ( QuickOpenResultItem ) ;
item - > connect ( SceneStringName ( gui_input ) , callable_mp ( this , & QuickOpenResultContainer : : _item_input ) . bind ( i ) ) ;
result_items . write [ i ] = item ;
2024-10-10 23:50:50 -07:00
if ( ! never_opened ) {
_layout_result_item ( item ) ;
2024-10-02 19:21:28 +02:00
}
2023-12-02 21:33:38 +01:00
}
}
void QuickOpenResultContainer : : init ( const Vector < StringName > & p_base_types ) {
2024-10-10 23:50:50 -07:00
_ensure_result_vector_capacity ( ) ;
2023-12-02 21:33:38 +01:00
base_types = p_base_types ;
const int display_mode_behavior = EDITOR_GET ( " filesystem/quick_open_dialog/default_display_mode " ) ;
const bool adaptive_display_mode = ( display_mode_behavior = = 0 ) ;
2024-12-11 15:11:47 +01:00
const bool first_open = never_opened ;
2023-12-02 21:33:38 +01:00
if ( adaptive_display_mode ) {
_set_display_mode ( get_adaptive_display_mode ( p_base_types ) ) ;
2024-10-10 23:50:50 -07:00
} else if ( never_opened ) {
int last = EditorSettings : : get_singleton ( ) - > get_project_metadata ( " quick_open_dialog " , " last_mode " , ( int ) QuickOpenDisplayMode : : LIST ) ;
_set_display_mode ( ( QuickOpenDisplayMode ) last ) ;
2023-12-02 21:33:38 +01:00
}
2024-10-10 23:50:50 -07:00
const bool fuzzy_matching = EDITOR_GET ( " filesystem/quick_open_dialog/enable_fuzzy_matching " ) ;
2023-12-02 21:33:38 +01:00
const bool include_addons = EDITOR_GET ( " filesystem/quick_open_dialog/include_addons " ) ;
2024-10-10 23:50:50 -07:00
fuzzy_search_toggle - > set_pressed_no_signal ( fuzzy_matching ) ;
2023-12-02 21:33:38 +01:00
include_addons_toggle - > set_pressed_no_signal ( include_addons ) ;
2024-10-10 23:50:50 -07:00
never_opened = false ;
const bool enable_highlights = EDITOR_GET ( " filesystem/quick_open_dialog/show_search_highlight " ) ;
for ( QuickOpenResultItem * E : result_items ) {
E - > enable_highlights = enable_highlights ;
}
2023-12-02 21:33:38 +01:00
2024-12-11 15:11:47 +01:00
if ( first_open & & history_file - > load ( _get_cache_file_path ( ) ) = = OK ) {
// Load history when opening for the first time.
file_type_icons . insert ( SNAME ( " __default_icon " ) , get_editor_theme_icon ( SNAME ( " Object " ) ) ) ;
2025-04-03 15:31:36 +02:00
bool history_modified = false ;
2025-04-23 23:32:05 -04:00
Vector < String > history_keys = history_file - > get_section_keys ( " selected_history " ) ;
2024-12-11 15:11:47 +01:00
for ( const String & type : history_keys ) {
const StringName type_name = type ;
const PackedStringArray paths = history_file - > get_value ( " selected_history " , type ) ;
2025-04-03 15:31:36 +02:00
PackedStringArray cleaned_paths ;
cleaned_paths . resize ( paths . size ( ) ) ;
2024-12-11 15:11:47 +01:00
Vector < QuickOpenResultCandidate > loaded_candidates ;
loaded_candidates . resize ( paths . size ( ) ) ;
{
QuickOpenResultCandidate * candidates_write = loaded_candidates . ptrw ( ) ;
2025-04-03 15:31:36 +02:00
String * cleanup_write = cleaned_paths . ptrw ( ) ;
2024-12-11 15:11:47 +01:00
int i = 0 ;
2025-04-03 15:31:36 +02:00
for ( String path : paths ) {
if ( path . begins_with ( " uid:// " ) ) {
ResourceUID : : ID id = ResourceUID : : get_singleton ( ) - > text_to_id ( path ) ;
if ( ! ResourceUID : : get_singleton ( ) - > has_id ( id ) ) {
continue ;
}
path = ResourceUID : : get_singleton ( ) - > get_id_path ( id ) ;
}
2025-02-10 22:16:24 +01:00
if ( ! ResourceLoader : : exists ( path ) ) {
continue ;
}
2024-12-11 15:11:47 +01:00
filetypes . insert ( path , type_name ) ;
QuickOpenResultCandidate candidate ;
_setup_candidate ( candidate , path ) ;
candidates_write [ i ] = candidate ;
2025-04-03 15:31:36 +02:00
cleanup_write [ i ] = path ;
2024-12-11 15:11:47 +01:00
i + + ;
}
2025-02-10 22:16:24 +01:00
loaded_candidates . resize ( i ) ;
2025-04-03 15:31:36 +02:00
cleaned_paths . resize ( i ) ;
2024-12-11 15:11:47 +01:00
selected_history . insert ( type , loaded_candidates ) ;
2025-04-03 15:31:36 +02:00
if ( i < paths . size ( ) ) {
// Some paths removed, need to update history.
if ( i = = 0 ) {
history_file - > erase_section_key ( " selected_history " , type ) ;
} else {
history_file - > set_value ( " selected_history " , type , cleaned_paths ) ;
}
history_modified = true ;
}
2024-12-11 15:11:47 +01:00
}
}
2025-04-03 15:31:36 +02:00
if ( history_modified ) {
history_file - > save ( _get_cache_file_path ( ) ) ;
}
2024-12-11 15:11:47 +01:00
}
2025-04-03 15:31:36 +02:00
2024-10-10 23:50:50 -07:00
_create_initial_results ( ) ;
2023-12-02 21:33:38 +01:00
}
2025-03-02 16:13:28 -08:00
void QuickOpenResultContainer : : _sort_filepaths ( int p_max_results ) {
struct FilepathComparator {
bool operator ( ) ( const String & p_lhs , const String & p_rhs ) const {
// Sort on (length, alphanumeric) to prioritize shorter filepaths
return p_lhs . length ( ) = = p_rhs . length ( ) ? p_lhs < p_rhs : p_lhs . length ( ) < p_rhs . length ( ) ;
}
} ;
SortArray < String , FilepathComparator > sorter ;
if ( filepaths . size ( ) > p_max_results ) {
sorter . partial_sort ( 0 , filepaths . size ( ) , p_max_results , filepaths . ptrw ( ) ) ;
} else {
sorter . sort ( filepaths . ptrw ( ) , filepaths . size ( ) ) ;
}
}
2024-10-10 23:50:50 -07:00
void QuickOpenResultContainer : : _create_initial_results ( ) {
2023-12-02 21:33:38 +01:00
file_type_icons . clear ( ) ;
2024-12-11 15:11:47 +01:00
file_type_icons . insert ( SNAME ( " __default_icon " ) , get_editor_theme_icon ( SNAME ( " Object " ) ) ) ;
2024-10-10 23:50:50 -07:00
filepaths . clear ( ) ;
filetypes . clear ( ) ;
2025-03-02 16:13:28 -08:00
history_set . clear ( ) ;
Vector < QuickOpenResultCandidate > * history = _get_history ( ) ;
if ( history ) {
for ( const QuickOpenResultCandidate & candidate : * history ) {
history_set . insert ( candidate . file_path ) ;
}
}
2024-10-10 23:50:50 -07:00
_find_filepaths_in_folder ( EditorFileSystem : : get_singleton ( ) - > get_filesystem ( ) , include_addons_toggle - > is_pressed ( ) ) ;
2025-03-02 16:13:28 -08:00
_sort_filepaths ( result_items . size ( ) ) ;
2024-10-10 23:50:50 -07:00
max_total_results = MIN ( filepaths . size ( ) , result_items . size ( ) ) ;
update_results ( ) ;
2023-12-02 21:33:38 +01:00
}
2024-10-10 23:50:50 -07:00
void QuickOpenResultContainer : : _find_filepaths_in_folder ( EditorFileSystemDirectory * p_directory , bool p_include_addons ) {
2023-12-02 21:33:38 +01:00
for ( int i = 0 ; i < p_directory - > get_subdir_count ( ) ; i + + ) {
if ( p_include_addons | | p_directory - > get_name ( ) ! = " addons " ) {
2024-10-10 23:50:50 -07:00
_find_filepaths_in_folder ( p_directory - > get_subdir ( i ) , p_include_addons ) ;
2023-12-02 21:33:38 +01:00
}
}
for ( int i = 0 ; i < p_directory - > get_file_count ( ) ; i + + ) {
String file_path = p_directory - > get_file_path ( i ) ;
const StringName engine_type = p_directory - > get_file_type ( i ) ;
const StringName script_type = p_directory - > get_file_resource_script_class ( i ) ;
const bool is_engine_type = script_type = = StringName ( ) ;
const StringName & actual_type = is_engine_type ? engine_type : script_type ;
for ( const StringName & parent_type : base_types ) {
bool is_valid = ClassDB : : is_parent_class ( engine_type , parent_type ) | | ( ! is_engine_type & & EditorNode : : get_editor_data ( ) . script_class_is_parent ( script_type , parent_type ) ) ;
if ( is_valid ) {
2024-10-10 23:50:50 -07:00
filepaths . append ( file_path ) ;
filetypes . insert ( file_path , actual_type ) ;
2023-12-02 21:33:38 +01:00
break ; // Stop testing base types as soon as we get a match.
}
}
}
}
2024-10-10 23:50:50 -07:00
void QuickOpenResultContainer : : set_query_and_update ( const String & p_query ) {
2023-12-02 21:33:38 +01:00
query = p_query ;
2024-10-10 23:50:50 -07:00
update_results ( ) ;
}
2025-03-02 16:13:28 -08:00
Vector < QuickOpenResultCandidate > * QuickOpenResultContainer : : _get_history ( ) {
if ( base_types . size ( ) = = 1 ) {
2025-05-31 15:50:10 +02:00
return selected_history . getptr ( base_types [ 0 ] ) ;
2025-02-10 22:16:24 +01:00
}
2025-03-02 16:13:28 -08:00
return nullptr ;
}
2024-10-10 23:50:50 -07:00
2025-03-02 16:13:28 -08:00
void QuickOpenResultContainer : : _setup_candidate ( QuickOpenResultCandidate & p_candidate , const String & p_filepath ) {
p_candidate . file_path = ResourceUID : : ensure_path ( p_filepath ) ;
p_candidate . result = nullptr ;
2024-12-11 15:11:47 +01:00
StringName actual_type ;
{
2025-05-31 15:50:10 +02:00
StringName * actual_type_ptr = filetypes . getptr ( p_filepath ) ;
2024-12-11 15:11:47 +01:00
if ( actual_type_ptr ) {
actual_type = * actual_type_ptr ;
} else {
2025-03-02 16:13:28 -08:00
ERR_PRINT ( vformat ( " EditorQuickOpenDialog: No type for path %s. " , p_candidate . file_path ) ) ;
2024-12-11 15:11:47 +01:00
}
}
2025-03-02 16:13:28 -08:00
EditorResourcePreview : : PreviewItem item = EditorResourcePreview : : get_singleton ( ) - > get_resource_preview_if_available ( p_candidate . file_path ) ;
2024-10-10 23:50:50 -07:00
if ( item . preview . is_valid ( ) ) {
2024-12-11 15:11:47 +01:00
p_candidate . thumbnail = item . preview ;
2024-10-10 23:50:50 -07:00
} else if ( file_type_icons . has ( actual_type ) ) {
2025-05-31 15:50:10 +02:00
p_candidate . thumbnail = * file_type_icons . getptr ( actual_type ) ;
2024-10-10 23:50:50 -07:00
} else if ( has_theme_icon ( actual_type , EditorStringName ( EditorIcons ) ) ) {
2024-12-11 15:11:47 +01:00
p_candidate . thumbnail = get_editor_theme_icon ( actual_type ) ;
file_type_icons . insert ( actual_type , p_candidate . thumbnail ) ;
2024-10-10 23:50:50 -07:00
} else {
2025-05-31 15:50:10 +02:00
p_candidate . thumbnail = * file_type_icons . getptr ( SNAME ( " __default_icon " ) ) ;
2023-12-02 21:33:38 +01:00
}
2024-10-10 23:50:50 -07:00
}
2023-12-02 21:33:38 +01:00
2024-10-10 23:50:50 -07:00
void QuickOpenResultContainer : : _setup_candidate ( QuickOpenResultCandidate & p_candidate , const FuzzySearchResult & p_result ) {
_setup_candidate ( p_candidate , p_result . target ) ;
p_candidate . result = & p_result ;
}
2023-12-02 21:33:38 +01:00
2024-10-10 23:50:50 -07:00
void QuickOpenResultContainer : : update_results ( ) {
candidates . clear ( ) ;
if ( query . is_empty ( ) ) {
_use_default_candidates ( ) ;
} else {
_score_and_sort_candidates ( ) ;
2023-12-02 21:33:38 +01:00
}
2024-10-10 23:50:50 -07:00
_update_result_items ( MIN ( candidates . size ( ) , max_total_results ) , 0 ) ;
}
2023-12-02 21:33:38 +01:00
2024-10-10 23:50:50 -07:00
void QuickOpenResultContainer : : _use_default_candidates ( ) {
2025-03-02 16:13:28 -08:00
Vector < QuickOpenResultCandidate > * history = _get_history ( ) ;
if ( history ) {
candidates . append_array ( * history ) ;
}
int count = candidates . size ( ) ;
candidates . resize ( MIN ( max_total_results , filepaths . size ( ) ) ) ;
for ( const String & filepath : filepaths ) {
if ( count > = max_total_results ) {
break ;
2023-12-02 21:33:38 +01:00
}
2025-03-02 16:13:28 -08:00
if ( ! history | | ! history_set . has ( filepath ) ) {
_setup_candidate ( candidates . write [ count + + ] , filepath ) ;
2023-12-02 21:33:38 +01:00
}
}
}
2024-10-10 23:50:50 -07:00
void QuickOpenResultContainer : : _update_fuzzy_search_results ( ) {
FuzzySearch fuzzy_search ;
fuzzy_search . start_offset = 6 ; // Don't match against "res://" at the start of each filepath.
fuzzy_search . set_query ( query ) ;
fuzzy_search . max_results = max_total_results ;
bool fuzzy_matching = EDITOR_GET ( " filesystem/quick_open_dialog/enable_fuzzy_matching " ) ;
int max_misses = EDITOR_GET ( " filesystem/quick_open_dialog/max_fuzzy_misses " ) ;
fuzzy_search . allow_subsequences = fuzzy_matching ;
fuzzy_search . max_misses = fuzzy_matching ? max_misses : 0 ;
fuzzy_search . search_all ( filepaths , search_results ) ;
}
2023-12-02 21:33:38 +01:00
2024-10-10 23:50:50 -07:00
void QuickOpenResultContainer : : _score_and_sort_candidates ( ) {
_update_fuzzy_search_results ( ) ;
candidates . resize ( search_results . size ( ) ) ;
QuickOpenResultCandidate * candidates_write = candidates . ptrw ( ) ;
for ( const FuzzySearchResult & result : search_results ) {
_setup_candidate ( * candidates_write + + , result ) ;
2023-12-02 21:33:38 +01:00
}
2024-10-10 23:50:50 -07:00
}
2023-12-02 21:33:38 +01:00
2024-10-10 23:50:50 -07:00
void QuickOpenResultContainer : : _update_result_items ( int p_new_visible_results_count , int p_new_selection_index ) {
2023-12-02 21:33:38 +01:00
// Only need to update items that were not hidden in previous update.
int num_items_needing_updates = MAX ( num_visible_results , p_new_visible_results_count ) ;
num_visible_results = p_new_visible_results_count ;
for ( int i = 0 ; i < num_items_needing_updates ; i + + ) {
QuickOpenResultItem * item = result_items [ i ] ;
if ( i < num_visible_results ) {
2024-10-10 23:50:50 -07:00
item - > set_content ( candidates [ i ] ) ;
2023-12-02 21:33:38 +01:00
} else {
item - > reset ( ) ;
}
} ;
const bool any_results = num_visible_results > 0 ;
_select_item ( any_results ? p_new_selection_index : - 1 ) ;
scroll_container - > set_visible ( any_results ) ;
no_results_container - > set_visible ( ! any_results ) ;
if ( ! any_results ) {
2024-10-10 23:50:50 -07:00
if ( filepaths . is_empty ( ) ) {
2023-12-02 21:33:38 +01:00
no_results_label - > set_text ( TTR ( " No files found for this type " ) ) ;
} else {
no_results_label - > set_text ( TTR ( " No results found " ) ) ;
}
}
}
void QuickOpenResultContainer : : handle_search_box_input ( const Ref < InputEvent > & p_ie ) {
if ( num_visible_results < 0 ) {
return ;
}
Ref < InputEventKey > key_event = p_ie ;
if ( key_event . is_valid ( ) & & key_event - > is_pressed ( ) ) {
bool move_selection = false ;
switch ( key_event - > get_keycode ( ) ) {
case Key : : UP :
case Key : : DOWN :
case Key : : PAGEUP :
case Key : : PAGEDOWN : {
move_selection = true ;
} break ;
case Key : : LEFT :
case Key : : RIGHT : {
2024-10-10 23:50:50 -07:00
if ( content_display_mode = = QuickOpenDisplayMode : : GRID ) {
// Maybe strip off the shift modifier to allow non-selecting navigation by character?
2024-08-20 09:50:44 -05:00
if ( key_event - > get_modifiers_mask ( ) . is_empty ( ) ) {
2024-10-10 23:50:50 -07:00
move_selection = true ;
}
}
2023-12-02 21:33:38 +01:00
} break ;
default :
break ; // Let the event through so it will reach the search box.
}
if ( move_selection ) {
_move_selection_index ( key_event - > get_keycode ( ) ) ;
queue_redraw ( ) ;
accept_event ( ) ;
}
}
}
void QuickOpenResultContainer : : _move_selection_index ( Key p_key ) {
2024-10-31 00:59:44 -07:00
// Don't move selection if there are no results.
if ( num_visible_results < = 0 ) {
return ;
}
2023-12-02 21:33:38 +01:00
const int max_index = num_visible_results - 1 ;
int idx = selection_index ;
if ( content_display_mode = = QuickOpenDisplayMode : : LIST ) {
if ( p_key = = Key : : UP ) {
idx = ( idx = = 0 ) ? max_index : ( idx - 1 ) ;
} else if ( p_key = = Key : : DOWN ) {
idx = ( idx = = max_index ) ? 0 : ( idx + 1 ) ;
} else if ( p_key = = Key : : PAGEUP ) {
idx = ( idx = = 0 ) ? idx : MAX ( idx - 10 , 0 ) ;
} else if ( p_key = = Key : : PAGEDOWN ) {
idx = ( idx = = max_index ) ? idx : MIN ( idx + 10 , max_index ) ;
}
} else {
int column_count = grid - > get_line_max_child_count ( ) ;
if ( p_key = = Key : : LEFT ) {
idx = ( idx = = 0 ) ? max_index : ( idx - 1 ) ;
} else if ( p_key = = Key : : RIGHT ) {
idx = ( idx = = max_index ) ? 0 : ( idx + 1 ) ;
} else if ( p_key = = Key : : UP ) {
idx = ( idx = = 0 ) ? max_index : MAX ( idx - column_count , 0 ) ;
} else if ( p_key = = Key : : DOWN ) {
idx = ( idx = = max_index ) ? 0 : MIN ( idx + column_count , max_index ) ;
} else if ( p_key = = Key : : PAGEUP ) {
idx = ( idx = = 0 ) ? idx : MAX ( idx - ( 3 * column_count ) , 0 ) ;
} else if ( p_key = = Key : : PAGEDOWN ) {
idx = ( idx = = max_index ) ? idx : MIN ( idx + ( 3 * column_count ) , max_index ) ;
}
}
_select_item ( idx ) ;
}
void QuickOpenResultContainer : : _select_item ( int p_index ) {
if ( ! has_nothing_selected ( ) ) {
result_items [ selection_index ] - > highlight_item ( false ) ;
}
selection_index = p_index ;
if ( has_nothing_selected ( ) ) {
file_details_path - > set_text ( " " ) ;
return ;
}
result_items [ selection_index ] - > highlight_item ( true ) ;
2025-03-02 16:13:28 -08:00
bool in_history = history_set . has ( candidates [ selection_index ] . file_path ) ;
file_details_path - > set_text ( get_selected ( ) + ( in_history ? TTR ( " (recently opened) " ) : " " ) ) ;
2023-12-02 21:33:38 +01:00
const QuickOpenResultItem * item = result_items [ selection_index ] ;
// Copied from Tree.
const int selected_position = item - > get_position ( ) . y ;
const int selected_size = item - > get_size ( ) . y ;
const int scroll_window_size = scroll_container - > get_size ( ) . y ;
const int scroll_position = scroll_container - > get_v_scroll ( ) ;
if ( selected_position < = scroll_position ) {
scroll_container - > set_v_scroll ( selected_position ) ;
} else if ( selected_position + selected_size > scroll_position + scroll_window_size ) {
scroll_container - > set_v_scroll ( selected_position + selected_size - scroll_window_size ) ;
}
}
void QuickOpenResultContainer : : _item_input ( const Ref < InputEvent > & p_ev , int p_index ) {
Ref < InputEventMouseButton > mb = p_ev ;
2024-12-21 01:43:50 +01:00
if ( mb . is_valid ( ) & & mb - > is_pressed ( ) ) {
if ( mb - > get_button_index ( ) = = MouseButton : : LEFT ) {
_select_item ( p_index ) ;
emit_signal ( SNAME ( " result_clicked " ) ) ;
} else if ( mb - > get_button_index ( ) = = MouseButton : : RIGHT ) {
_select_item ( p_index ) ;
file_context_menu - > set_position ( result_items [ p_index ] - > get_screen_position ( ) + mb - > get_position ( ) ) ;
file_context_menu - > reset_size ( ) ;
file_context_menu - > popup ( ) ;
}
2023-12-02 21:33:38 +01:00
}
}
2024-10-10 23:50:50 -07:00
void QuickOpenResultContainer : : _toggle_fuzzy_search ( bool p_pressed ) {
EditorSettings : : get_singleton ( ) - > set ( " filesystem/quick_open_dialog/enable_fuzzy_matching " , p_pressed ) ;
update_results ( ) ;
}
2024-12-11 15:11:47 +01:00
String QuickOpenResultContainer : : _get_cache_file_path ( ) const {
return EditorPaths : : get_singleton ( ) - > get_project_settings_dir ( ) . path_join ( " quick_open_dialog_cache.cfg " ) ;
}
2023-12-02 21:33:38 +01:00
void QuickOpenResultContainer : : _toggle_include_addons ( bool p_pressed ) {
EditorSettings : : get_singleton ( ) - > set ( " filesystem/quick_open_dialog/include_addons " , p_pressed ) ;
cleanup ( ) ;
2024-10-10 23:50:50 -07:00
_create_initial_results ( ) ;
2023-12-02 21:33:38 +01:00
}
void QuickOpenResultContainer : : _toggle_display_mode ( ) {
QuickOpenDisplayMode new_display_mode = ( content_display_mode = = QuickOpenDisplayMode : : LIST ) ? QuickOpenDisplayMode : : GRID : QuickOpenDisplayMode : : LIST ;
_set_display_mode ( new_display_mode ) ;
}
2024-10-10 23:50:50 -07:00
CanvasItem * QuickOpenResultContainer : : _get_result_root ( ) {
if ( content_display_mode = = QuickOpenDisplayMode : : LIST ) {
return list ;
} else {
return grid ;
}
}
2023-12-02 21:33:38 +01:00
2024-10-10 23:50:50 -07:00
void QuickOpenResultContainer : : _layout_result_item ( QuickOpenResultItem * item ) {
item - > set_display_mode ( content_display_mode ) ;
Node * parent = item - > get_parent ( ) ;
if ( parent ) {
parent - > remove_child ( item ) ;
2023-12-02 21:33:38 +01:00
}
2024-10-10 23:50:50 -07:00
_get_result_root ( ) - > add_child ( item ) ;
}
2023-12-02 21:33:38 +01:00
2024-10-10 23:50:50 -07:00
void QuickOpenResultContainer : : _set_display_mode ( QuickOpenDisplayMode p_display_mode ) {
CanvasItem * prev_root = _get_result_root ( ) ;
2023-12-02 21:33:38 +01:00
2024-10-10 23:50:50 -07:00
if ( prev_root - > is_visible ( ) & & content_display_mode = = p_display_mode ) {
return ;
2023-12-02 21:33:38 +01:00
}
2024-10-10 23:50:50 -07:00
content_display_mode = p_display_mode ;
CanvasItem * next_root = _get_result_root ( ) ;
2024-10-02 19:21:28 +02:00
2024-10-10 23:50:50 -07:00
EditorSettings : : get_singleton ( ) - > set_project_metadata ( " quick_open_dialog " , " last_mode " , ( int ) content_display_mode ) ;
2023-12-02 21:33:38 +01:00
2024-10-10 23:50:50 -07:00
prev_root - > hide ( ) ;
next_root - > show ( ) ;
2023-12-02 21:33:38 +01:00
2024-10-10 23:50:50 -07:00
for ( QuickOpenResultItem * item : result_items ) {
_layout_result_item ( item ) ;
2023-12-02 21:33:38 +01:00
}
_update_result_items ( num_visible_results , selection_index ) ;
if ( content_display_mode = = QuickOpenDisplayMode : : LIST ) {
2024-10-09 15:21:47 -07:00
display_mode_toggle - > set_button_icon ( get_editor_theme_icon ( SNAME ( " FileThumbnail " ) ) ) ;
2023-12-02 21:33:38 +01:00
display_mode_toggle - > set_tooltip_text ( TTR ( " Grid view " ) ) ;
} else {
2024-10-09 15:21:47 -07:00
display_mode_toggle - > set_button_icon ( get_editor_theme_icon ( SNAME ( " FileList " ) ) ) ;
2023-12-02 21:33:38 +01:00
display_mode_toggle - > set_tooltip_text ( TTR ( " List view " ) ) ;
}
}
bool QuickOpenResultContainer : : has_nothing_selected ( ) const {
return selection_index < 0 ;
}
String QuickOpenResultContainer : : get_selected ( ) const {
ERR_FAIL_COND_V_MSG ( has_nothing_selected ( ) , String ( ) , " Tried to get selected file, but nothing was selected. " ) ;
2025-03-02 16:13:28 -08:00
return candidates [ selection_index ] . file_path ;
2023-12-02 21:33:38 +01:00
}
QuickOpenDisplayMode QuickOpenResultContainer : : get_adaptive_display_mode ( const Vector < StringName > & p_base_types ) {
static const Vector < StringName > grid_preferred_types = {
2025-04-28 19:11:24 +08:00
StringName ( " Font " , true ) ,
StringName ( " Texture2D " , true ) ,
StringName ( " Material " , true ) ,
StringName ( " Mesh " , true ) ,
2023-12-02 21:33:38 +01:00
} ;
for ( const StringName & type : grid_preferred_types ) {
for ( const StringName & base_type : p_base_types ) {
2024-06-21 11:19:04 -05:00
if ( base_type = = type | | ClassDB : : is_parent_class ( base_type , type ) ) {
2023-12-02 21:33:38 +01:00
return QuickOpenDisplayMode : : GRID ;
2024-06-21 11:19:04 -05:00
}
2023-12-02 21:33:38 +01:00
}
}
return QuickOpenDisplayMode : : LIST ;
}
2025-03-02 16:13:28 -08:00
String _get_uid_string ( const String & p_filepath ) {
ResourceUID : : ID id = EditorFileSystem : : get_singleton ( ) - > get_file_uid ( p_filepath ) ;
return id = = ResourceUID : : INVALID_ID ? p_filepath : ResourceUID : : get_singleton ( ) - > id_to_text ( id ) ;
}
2023-12-02 21:33:38 +01:00
void QuickOpenResultContainer : : save_selected_item ( ) {
if ( base_types . size ( ) > 1 ) {
// Getting the type of the file and checking which base type it belongs to should be possible.
// However, for now these are not supported, and we don't record this.
return ;
}
const StringName & base_type = base_types [ 0 ] ;
2024-12-11 15:11:47 +01:00
QuickOpenResultCandidate & selected = candidates . write [ selection_index ] ;
2025-05-31 15:50:10 +02:00
Vector < QuickOpenResultCandidate > * type_history = selected_history . getptr ( base_type ) ;
2023-12-02 21:33:38 +01:00
if ( ! type_history ) {
2024-10-10 23:50:50 -07:00
selected_history . insert ( base_type , Vector < QuickOpenResultCandidate > ( ) ) ;
2025-05-31 15:50:10 +02:00
type_history = selected_history . getptr ( base_type ) ;
2023-12-02 21:33:38 +01:00
} else {
2024-10-10 23:50:50 -07:00
for ( int i = 0 ; i < type_history - > size ( ) ; i + + ) {
if ( selected . file_path = = type_history - > get ( i ) . file_path ) {
type_history - > remove_at ( i ) ;
break ;
2023-12-02 21:33:38 +01:00
}
}
}
2024-12-11 15:11:47 +01:00
selected . result = nullptr ;
2025-03-02 16:13:28 -08:00
history_set . insert ( selected . file_path ) ;
2024-10-10 23:50:50 -07:00
type_history - > insert ( 0 , selected ) ;
if ( type_history - > size ( ) > MAX_HISTORY_SIZE ) {
type_history - > resize ( MAX_HISTORY_SIZE ) ;
}
2024-12-11 15:11:47 +01:00
PackedStringArray paths ;
paths . resize ( type_history - > size ( ) ) ;
{
String * paths_write = paths . ptrw ( ) ;
int i = 0 ;
for ( const QuickOpenResultCandidate & candidate : * type_history ) {
2025-03-02 16:13:28 -08:00
paths_write [ i ] = _get_uid_string ( candidate . file_path ) ;
2024-12-11 15:11:47 +01:00
i + + ;
}
}
history_file - > set_value ( " selected_history " , base_type , paths ) ;
history_file - > save ( _get_cache_file_path ( ) ) ;
2023-12-02 21:33:38 +01:00
}
void QuickOpenResultContainer : : cleanup ( ) {
num_visible_results = 0 ;
candidates . clear ( ) ;
2025-03-02 16:13:28 -08:00
history_set . clear ( ) ;
2023-12-02 21:33:38 +01:00
_select_item ( - 1 ) ;
for ( QuickOpenResultItem * item : result_items ) {
item - > reset ( ) ;
}
}
void QuickOpenResultContainer : : _notification ( int p_what ) {
switch ( p_what ) {
case NOTIFICATION_THEME_CHANGED : {
Color text_color = get_theme_color ( " font_readonly_color " , EditorStringName ( Editor ) ) ;
file_details_path - > add_theme_color_override ( SceneStringName ( font_color ) , text_color ) ;
no_results_label - > add_theme_color_override ( SceneStringName ( font_color ) , text_color ) ;
2024-10-04 22:41:57 +04:00
panel_container - > add_theme_style_override ( SceneStringName ( panel ) , get_theme_stylebox ( SceneStringName ( panel ) , SNAME ( " Tree " ) ) ) ;
2023-12-02 21:33:38 +01:00
if ( content_display_mode = = QuickOpenDisplayMode : : LIST ) {
2024-10-09 15:21:47 -07:00
display_mode_toggle - > set_button_icon ( get_editor_theme_icon ( SNAME ( " FileThumbnail " ) ) ) ;
2023-12-02 21:33:38 +01:00
} else {
2024-10-09 15:21:47 -07:00
display_mode_toggle - > set_button_icon ( get_editor_theme_icon ( SNAME ( " FileList " ) ) ) ;
2023-12-02 21:33:38 +01:00
}
} break ;
}
}
void QuickOpenResultContainer : : _bind_methods ( ) {
ADD_SIGNAL ( MethodInfo ( " result_clicked " ) ) ;
}
//------------------------- Result Item
QuickOpenResultItem : : QuickOpenResultItem ( ) {
set_focus_mode ( FocusMode : : FOCUS_ALL ) ;
_set_enabled ( false ) ;
set_default_cursor_shape ( CURSOR_POINTING_HAND ) ;
list_item = memnew ( QuickOpenResultListItem ) ;
list_item - > hide ( ) ;
add_child ( list_item ) ;
grid_item = memnew ( QuickOpenResultGridItem ) ;
grid_item - > hide ( ) ;
add_child ( grid_item ) ;
}
void QuickOpenResultItem : : set_display_mode ( QuickOpenDisplayMode p_display_mode ) {
if ( p_display_mode = = QuickOpenDisplayMode : : LIST ) {
grid_item - > hide ( ) ;
2024-10-10 23:50:50 -07:00
grid_item - > reset ( ) ;
2023-12-02 21:33:38 +01:00
list_item - > show ( ) ;
} else {
list_item - > hide ( ) ;
2024-10-10 23:50:50 -07:00
list_item - > reset ( ) ;
2023-12-02 21:33:38 +01:00
grid_item - > show ( ) ;
}
queue_redraw ( ) ;
}
2024-10-10 23:50:50 -07:00
void QuickOpenResultItem : : set_content ( const QuickOpenResultCandidate & p_candidate ) {
2023-12-02 21:33:38 +01:00
_set_enabled ( true ) ;
if ( list_item - > is_visible ( ) ) {
2024-10-10 23:50:50 -07:00
list_item - > set_content ( p_candidate , enable_highlights ) ;
2023-12-02 21:33:38 +01:00
} else {
2024-10-10 23:50:50 -07:00
grid_item - > set_content ( p_candidate , enable_highlights ) ;
2023-12-02 21:33:38 +01:00
}
2024-10-10 23:50:50 -07:00
queue_redraw ( ) ;
2023-12-02 21:33:38 +01:00
}
void QuickOpenResultItem : : reset ( ) {
_set_enabled ( false ) ;
is_hovering = false ;
is_selected = false ;
2024-10-10 23:50:50 -07:00
list_item - > reset ( ) ;
grid_item - > reset ( ) ;
2023-12-02 21:33:38 +01:00
}
void QuickOpenResultItem : : highlight_item ( bool p_enabled ) {
is_selected = p_enabled ;
if ( list_item - > is_visible ( ) ) {
if ( p_enabled ) {
list_item - > highlight_item ( highlighted_font_color ) ;
} else {
list_item - > remove_highlight ( ) ;
}
} else {
if ( p_enabled ) {
grid_item - > highlight_item ( highlighted_font_color ) ;
} else {
grid_item - > remove_highlight ( ) ;
}
}
queue_redraw ( ) ;
}
void QuickOpenResultItem : : _set_enabled ( bool p_enabled ) {
set_visible ( p_enabled ) ;
set_process ( p_enabled ) ;
set_process_input ( p_enabled ) ;
}
void QuickOpenResultItem : : _notification ( int p_what ) {
switch ( p_what ) {
case NOTIFICATION_MOUSE_ENTER :
case NOTIFICATION_MOUSE_EXIT : {
is_hovering = is_visible ( ) & & p_what = = NOTIFICATION_MOUSE_ENTER ;
queue_redraw ( ) ;
} break ;
case NOTIFICATION_THEME_CHANGED : {
selected_stylebox = get_theme_stylebox ( " selected " , " Tree " ) ;
2025-01-15 15:36:54 +02:00
hovering_stylebox = get_theme_stylebox ( SNAME ( " hovered " ) , " Tree " ) ;
2023-12-02 21:33:38 +01:00
highlighted_font_color = get_theme_color ( " font_focus_color " , EditorStringName ( Editor ) ) ;
} break ;
case NOTIFICATION_DRAW : {
if ( is_selected ) {
draw_style_box ( selected_stylebox , Rect2 ( Point2 ( ) , get_size ( ) ) ) ;
} else if ( is_hovering ) {
draw_style_box ( hovering_stylebox , Rect2 ( Point2 ( ) , get_size ( ) ) ) ;
}
} break ;
}
}
//----------------- List item
2024-10-10 23:50:50 -07:00
static Vector2i _get_path_interval ( const Vector2i & p_interval , int p_dir_index ) {
if ( p_interval . x > = p_dir_index | | p_interval . y < 1 ) {
return { - 1 , - 1 } ;
}
return { p_interval . x , MIN ( p_interval . x + p_interval . y , p_dir_index ) - p_interval . x } ;
}
static Vector2i _get_name_interval ( const Vector2i & p_interval , int p_dir_index ) {
if ( p_interval . x + p_interval . y < = p_dir_index | | p_interval . y < 1 ) {
return { - 1 , - 1 } ;
}
int first_name_idx = p_dir_index + 1 ;
int start = MAX ( p_interval . x , first_name_idx ) ;
return { start - first_name_idx , p_interval . y - start + p_interval . x } ;
}
2023-12-02 21:33:38 +01:00
QuickOpenResultListItem : : QuickOpenResultListItem ( ) {
set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
2025-01-15 15:36:54 +02:00
add_theme_constant_override ( " margin_left " , 6 * EDSCALE ) ;
add_theme_constant_override ( " margin_right " , 6 * EDSCALE ) ;
2023-12-02 21:33:38 +01:00
2025-01-15 15:36:54 +02:00
hbc = memnew ( HBoxContainer ) ;
hbc - > add_theme_constant_override ( SNAME ( " separation " ) , 4 * EDSCALE ) ;
add_child ( hbc ) ;
2023-12-02 21:33:38 +01:00
2025-01-15 15:36:54 +02:00
const int max_size = 36 * EDSCALE ;
thumbnail = memnew ( TextureRect ) ;
thumbnail - > set_h_size_flags ( Control : : SIZE_SHRINK_CENTER ) ;
thumbnail - > set_v_size_flags ( Control : : SIZE_SHRINK_CENTER ) ;
thumbnail - > set_expand_mode ( TextureRect : : EXPAND_IGNORE_SIZE ) ;
thumbnail - > set_stretch_mode ( TextureRect : : STRETCH_KEEP_ASPECT_CENTERED ) ;
thumbnail - > set_custom_minimum_size ( Size2i ( max_size , max_size ) ) ;
hbc - > add_child ( thumbnail ) ;
text_container = memnew ( VBoxContainer ) ;
text_container - > add_theme_constant_override ( SNAME ( " separation " ) , - 7 * EDSCALE ) ;
text_container - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
text_container - > set_v_size_flags ( Control : : SIZE_FILL ) ;
hbc - > add_child ( text_container ) ;
name = memnew ( HighlightedLabel ) ;
name - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
name - > set_text_overrun_behavior ( TextServer : : OVERRUN_TRIM_ELLIPSIS ) ;
name - > set_horizontal_alignment ( HorizontalAlignment : : HORIZONTAL_ALIGNMENT_LEFT ) ;
text_container - > add_child ( name ) ;
path = memnew ( HighlightedLabel ) ;
path - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
path - > set_text_overrun_behavior ( TextServer : : OVERRUN_TRIM_ELLIPSIS ) ;
path - > add_theme_font_size_override ( SceneStringName ( font_size ) , 12 * EDSCALE ) ;
text_container - > add_child ( path ) ;
2023-12-02 21:33:38 +01:00
}
2024-10-10 23:50:50 -07:00
void QuickOpenResultListItem : : set_content ( const QuickOpenResultCandidate & p_candidate , bool p_highlight ) {
thumbnail - > set_texture ( p_candidate . thumbnail ) ;
2025-03-02 16:13:28 -08:00
name - > set_text ( p_candidate . file_path . get_file ( ) ) ;
path - > set_text ( p_candidate . file_path . get_base_dir ( ) ) ;
2024-10-10 23:50:50 -07:00
name - > reset_highlights ( ) ;
path - > reset_highlights ( ) ;
if ( p_highlight & & p_candidate . result ! = nullptr ) {
for ( const FuzzyTokenMatch & match : p_candidate . result - > token_matches ) {
for ( const Vector2i & interval : match . substrings ) {
path - > add_highlight ( _get_path_interval ( interval , p_candidate . result - > dir_index ) ) ;
name - > add_highlight ( _get_name_interval ( interval , p_candidate . result - > dir_index ) ) ;
}
}
}
2023-12-02 21:33:38 +01:00
}
void QuickOpenResultListItem : : reset ( ) {
thumbnail - > set_texture ( nullptr ) ;
2024-10-10 23:50:50 -07:00
name - > set_text ( " " ) ;
2023-12-02 21:33:38 +01:00
path - > set_text ( " " ) ;
2024-10-10 23:50:50 -07:00
name - > reset_highlights ( ) ;
path - > reset_highlights ( ) ;
2023-12-02 21:33:38 +01:00
}
void QuickOpenResultListItem : : highlight_item ( const Color & p_color ) {
name - > add_theme_color_override ( SceneStringName ( font_color ) , p_color ) ;
}
void QuickOpenResultListItem : : remove_highlight ( ) {
name - > remove_theme_color_override ( SceneStringName ( font_color ) ) ;
}
void QuickOpenResultListItem : : _notification ( int p_what ) {
switch ( p_what ) {
case NOTIFICATION_THEME_CHANGED : {
path - > add_theme_color_override ( SceneStringName ( font_color ) , get_theme_color ( " font_disabled_color " , EditorStringName ( Editor ) ) ) ;
} break ;
}
}
//--------------- Grid Item
QuickOpenResultGridItem : : QuickOpenResultGridItem ( ) {
2025-01-15 15:36:54 +02:00
set_custom_minimum_size ( Size2i ( 120 * EDSCALE , 0 ) ) ;
add_theme_constant_override ( " margin_top " , 6 * EDSCALE ) ;
add_theme_constant_override ( " margin_left " , 2 * EDSCALE ) ;
add_theme_constant_override ( " margin_right " , 2 * EDSCALE ) ;
vbc = memnew ( VBoxContainer ) ;
vbc - > set_h_size_flags ( Control : : SIZE_FILL ) ;
vbc - > set_v_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
vbc - > add_theme_constant_override ( SNAME ( " separation " ) , 0 ) ;
add_child ( vbc ) ;
const int max_size = 64 * EDSCALE ;
2023-12-02 21:33:38 +01:00
thumbnail = memnew ( TextureRect ) ;
thumbnail - > set_h_size_flags ( Control : : SIZE_SHRINK_CENTER ) ;
thumbnail - > set_v_size_flags ( Control : : SIZE_SHRINK_CENTER ) ;
2025-01-15 15:36:54 +02:00
thumbnail - > set_custom_minimum_size ( Size2i ( max_size , max_size ) ) ;
vbc - > add_child ( thumbnail ) ;
2023-12-02 21:33:38 +01:00
2024-10-10 23:50:50 -07:00
name = memnew ( HighlightedLabel ) ;
2023-12-02 21:33:38 +01:00
name - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
name - > set_text_overrun_behavior ( TextServer : : OVERRUN_TRIM_ELLIPSIS ) ;
name - > set_horizontal_alignment ( HorizontalAlignment : : HORIZONTAL_ALIGNMENT_CENTER ) ;
name - > add_theme_font_size_override ( SceneStringName ( font_size ) , 13 * EDSCALE ) ;
2025-01-15 15:36:54 +02:00
vbc - > add_child ( name ) ;
2023-12-02 21:33:38 +01:00
}
2024-10-10 23:50:50 -07:00
void QuickOpenResultGridItem : : set_content ( const QuickOpenResultCandidate & p_candidate , bool p_highlight ) {
thumbnail - > set_texture ( p_candidate . thumbnail ) ;
2025-03-02 16:13:28 -08:00
name - > set_text ( p_candidate . file_path . get_file ( ) ) ;
name - > set_tooltip_text ( p_candidate . file_path ) ;
2024-10-10 23:50:50 -07:00
name - > reset_highlights ( ) ;
2023-12-02 21:33:38 +01:00
2024-10-10 23:50:50 -07:00
if ( p_highlight & & p_candidate . result ! = nullptr ) {
for ( const FuzzyTokenMatch & match : p_candidate . result - > token_matches ) {
for ( const Vector2i & interval : match . substrings ) {
name - > add_highlight ( _get_name_interval ( interval , p_candidate . result - > dir_index ) ) ;
}
}
}
2023-12-02 21:33:38 +01:00
2024-10-10 23:50:50 -07:00
bool uses_icon = p_candidate . thumbnail - > get_width ( ) < ( 32 * EDSCALE ) ;
2023-12-02 21:33:38 +01:00
2024-10-10 23:50:50 -07:00
if ( uses_icon | | p_candidate . thumbnail - > get_height ( ) < = thumbnail - > get_custom_minimum_size ( ) . y ) {
2023-12-02 21:33:38 +01:00
thumbnail - > set_expand_mode ( TextureRect : : EXPAND_KEEP_SIZE ) ;
thumbnail - > set_stretch_mode ( TextureRect : : StretchMode : : STRETCH_KEEP_CENTERED ) ;
} else {
thumbnail - > set_expand_mode ( TextureRect : : EXPAND_FIT_WIDTH_PROPORTIONAL ) ;
thumbnail - > set_stretch_mode ( TextureRect : : StretchMode : : STRETCH_SCALE ) ;
}
}
void QuickOpenResultGridItem : : reset ( ) {
thumbnail - > set_texture ( nullptr ) ;
2024-10-10 23:50:50 -07:00
name - > set_text ( " " ) ;
name - > reset_highlights ( ) ;
2023-12-02 21:33:38 +01:00
}
void QuickOpenResultGridItem : : highlight_item ( const Color & p_color ) {
name - > add_theme_color_override ( SceneStringName ( font_color ) , p_color ) ;
}
void QuickOpenResultGridItem : : remove_highlight ( ) {
name - > remove_theme_color_override ( SceneStringName ( font_color ) ) ;
}