Merge pull request #69698 from Daylily-Zeleen/daylily-zeleen/show_in_explorer

Implement and expose OS::shell_show_in_file_manager()
This commit is contained in:
Rémi Verschelde 2023-04-25 09:57:27 +02:00
commit 76d33d187f
No known key found for this signature in database
GPG Key ID: C3336907360768E1
12 changed files with 87 additions and 15 deletions

View File

@ -257,6 +257,15 @@ Error OS::shell_open(String p_uri) {
return ::OS::get_singleton()->shell_open(p_uri); return ::OS::get_singleton()->shell_open(p_uri);
} }
Error OS::shell_show_in_file_manager(String p_path, bool p_open_folder) {
if (p_path.begins_with("res://")) {
WARN_PRINT("Attempting to explore file path with the \"res://\" protocol. Use `ProjectSettings.globalize_path()` to convert a Godot-specific path to a system path before opening it with `OS.shell_show_in_file_manager()`.");
} else if (p_path.begins_with("user://")) {
WARN_PRINT("Attempting to explore file path with the \"user://\" protocol. Use `ProjectSettings.globalize_path()` to convert a Godot-specific path to a system path before opening it with `OS.shell_show_in_file_manager()`.");
}
return ::OS::get_singleton()->shell_show_in_file_manager(p_path, p_open_folder);
}
String OS::read_string_from_stdin() { String OS::read_string_from_stdin() {
return ::OS::get_singleton()->get_stdin_string(); return ::OS::get_singleton()->get_stdin_string();
} }
@ -553,6 +562,7 @@ void OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("create_instance", "arguments"), &OS::create_instance); ClassDB::bind_method(D_METHOD("create_instance", "arguments"), &OS::create_instance);
ClassDB::bind_method(D_METHOD("kill", "pid"), &OS::kill); ClassDB::bind_method(D_METHOD("kill", "pid"), &OS::kill);
ClassDB::bind_method(D_METHOD("shell_open", "uri"), &OS::shell_open); ClassDB::bind_method(D_METHOD("shell_open", "uri"), &OS::shell_open);
ClassDB::bind_method(D_METHOD("shell_show_in_file_manager", "file_or_dir_path", "open_folder"), &OS::shell_show_in_file_manager, DEFVAL(true));
ClassDB::bind_method(D_METHOD("is_process_running", "pid"), &OS::is_process_running); ClassDB::bind_method(D_METHOD("is_process_running", "pid"), &OS::is_process_running);
ClassDB::bind_method(D_METHOD("get_process_id"), &OS::get_process_id); ClassDB::bind_method(D_METHOD("get_process_id"), &OS::get_process_id);

View File

@ -152,6 +152,7 @@ public:
int create_instance(const Vector<String> &p_arguments); int create_instance(const Vector<String> &p_arguments);
Error kill(int p_pid); Error kill(int p_pid);
Error shell_open(String p_uri); Error shell_open(String p_uri);
Error shell_show_in_file_manager(String p_path, bool p_open_folder = true);
bool is_process_running(int p_pid) const; bool is_process_running(int p_pid) const;
int get_process_id() const; int get_process_id() const;

View File

@ -281,6 +281,15 @@ Error OS::shell_open(String p_uri) {
return ERR_UNAVAILABLE; return ERR_UNAVAILABLE;
} }
Error OS::shell_show_in_file_manager(String p_path, bool p_open_folder) {
if (!p_path.begins_with("file://")) {
p_path = String("file://") + p_path;
}
if (!p_path.ends_with("/")) {
p_path = p_path.get_base_dir();
}
return shell_open(p_path);
}
// implement these with the canvas? // implement these with the canvas?
uint64_t OS::get_static_memory_usage() const { uint64_t OS::get_static_memory_usage() const {

View File

@ -163,6 +163,7 @@ public:
virtual void vibrate_handheld(int p_duration_ms = 500) {} virtual void vibrate_handheld(int p_duration_ms = 500) {}
virtual Error shell_open(String p_uri); virtual Error shell_open(String p_uri);
virtual Error shell_show_in_file_manager(String p_path, bool p_open_folder = true);
virtual Error set_cwd(const String &p_cwd); virtual Error set_cwd(const String &p_cwd);
virtual bool has_environment(const String &p_var) const = 0; virtual bool has_environment(const String &p_var) const = 0;

View File

@ -649,6 +649,17 @@
[b]Note:[/b] This method is implemented on Android, iOS, Web, Linux, macOS and Windows. [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux, macOS and Windows.
</description> </description>
</method> </method>
<method name="shell_show_in_file_manager">
<return type="int" enum="Error" />
<param index="0" name="file_or_dir_path" type="String" />
<param index="1" name="open_folder" type="bool" default="true" />
<description>
Requests the OS to open file manager, then navigate to the given [param file_or_dir_path] and select the target file or folder.
If [param file_or_dir_path] is a valid directory path, and [param open_folder] is [code]true[/code], the method will open explorer and enter the target folder without selecting anything.
Use [method ProjectSettings.globalize_path] to convert a [code]res://[/code] or [code]user://[/code] path into a system path for use with this method.
[b]Note:[/b] Currently this method is only implemented on Windows. On other platforms, it will fallback to [method shell_open] with a directory path for [param file_or_dir_path].
</description>
</method>
<method name="unset_environment" qualifiers="const"> <method name="unset_environment" qualifiers="const">
<return type="void" /> <return type="void" />
<param index="0" name="variable" type="String" /> <param index="0" name="variable" type="String" />

View File

@ -2995,10 +2995,10 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
case RUN_USER_DATA_FOLDER: { case RUN_USER_DATA_FOLDER: {
// Ensure_user_data_dir() to prevent the edge case: "Open User Data Folder" won't work after the project was renamed in ProjectSettingsEditor unless the project is saved. // Ensure_user_data_dir() to prevent the edge case: "Open User Data Folder" won't work after the project was renamed in ProjectSettingsEditor unless the project is saved.
OS::get_singleton()->ensure_user_data_dir(); OS::get_singleton()->ensure_user_data_dir();
OS::get_singleton()->shell_open(String("file://") + OS::get_singleton()->get_user_data_dir()); OS::get_singleton()->shell_show_in_file_manager(OS::get_singleton()->get_user_data_dir(), true);
} break; } break;
case FILE_EXPLORE_ANDROID_BUILD_TEMPLATES: { case FILE_EXPLORE_ANDROID_BUILD_TEMPLATES: {
OS::get_singleton()->shell_open("file://" + ProjectSettings::get_singleton()->get_resource_path().path_join("android")); OS::get_singleton()->shell_show_in_file_manager(ProjectSettings::get_singleton()->get_resource_path().path_join("android"), true);
} break; } break;
case FILE_QUIT: case FILE_QUIT:
case RUN_PROJECT_MANAGER: case RUN_PROJECT_MANAGER:
@ -3070,10 +3070,10 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
editor_settings_dialog->popup_edit_settings(); editor_settings_dialog->popup_edit_settings();
} break; } break;
case SETTINGS_EDITOR_DATA_FOLDER: { case SETTINGS_EDITOR_DATA_FOLDER: {
OS::get_singleton()->shell_open(String("file://") + EditorPaths::get_singleton()->get_data_dir()); OS::get_singleton()->shell_show_in_file_manager(EditorPaths::get_singleton()->get_data_dir(), true);
} break; } break;
case SETTINGS_EDITOR_CONFIG_FOLDER: { case SETTINGS_EDITOR_CONFIG_FOLDER: {
OS::get_singleton()->shell_open(String("file://") + EditorPaths::get_singleton()->get_config_dir()); OS::get_singleton()->shell_show_in_file_manager(EditorPaths::get_singleton()->get_config_dir(), true);
} break; } break;
case SETTINGS_MANAGE_EXPORT_TEMPLATES: { case SETTINGS_MANAGE_EXPORT_TEMPLATES: {
export_template_manager->popup_manager(); export_template_manager->popup_manager();
@ -3176,7 +3176,7 @@ void EditorNode::_screenshot(bool p_use_utc) {
NodePath path = String("user://") + name; NodePath path = String("user://") + name;
_save_screenshot(path); _save_screenshot(path);
if (EDITOR_GET("interface/editor/automatically_open_screenshots")) { if (EDITOR_GET("interface/editor/automatically_open_screenshots")) {
OS::get_singleton()->shell_open(String("file://") + ProjectSettings::get_singleton()->globalize_path(path)); OS::get_singleton()->shell_show_in_file_manager(ProjectSettings::get_singleton()->globalize_path(path), true);
} }
} }

View File

@ -620,7 +620,7 @@ void ExportTemplateManager::_installed_table_button_cbk(Object *p_item, int p_co
void ExportTemplateManager::_open_template_folder(const String &p_version) { void ExportTemplateManager::_open_template_folder(const String &p_version) {
const String &templates_dir = EditorPaths::get_singleton()->get_export_templates_dir(); const String &templates_dir = EditorPaths::get_singleton()->get_export_templates_dir();
OS::get_singleton()->shell_open("file://" + templates_dir.path_join(p_version)); OS::get_singleton()->shell_show_in_file_manager(templates_dir.path_join(p_version), true);
} }
void ExportTemplateManager::popup_manager() { void ExportTemplateManager::popup_manager() {

View File

@ -1881,11 +1881,8 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected
fpath = p_selected[0]; fpath = p_selected[0];
} }
if (!fpath.ends_with("/")) {
fpath = fpath.get_base_dir();
}
String dir = ProjectSettings::get_singleton()->globalize_path(fpath); String dir = ProjectSettings::get_singleton()->globalize_path(fpath);
OS::get_singleton()->shell_open(String("file://") + dir); OS::get_singleton()->shell_show_in_file_manager(dir, true);
} break; } break;
case FILE_OPEN_EXTERNAL: { case FILE_OPEN_EXTERNAL: {

View File

@ -713,11 +713,8 @@ void EditorFileDialog::_item_menu_id_pressed(int p_option) {
// Specific item was clicked. Open folders directly, or the folder containing a selected file. // Specific item was clicked. Open folders directly, or the folder containing a selected file.
Dictionary item_meta = item_list->get_item_metadata(idx); Dictionary item_meta = item_list->get_item_metadata(idx);
path = ProjectSettings::get_singleton()->globalize_path(item_meta["path"]); path = ProjectSettings::get_singleton()->globalize_path(item_meta["path"]);
if (!item_meta["dir"]) {
path = path.get_base_dir();
}
} }
OS::get_singleton()->shell_open(String("file://") + path); OS::get_singleton()->shell_show_in_file_manager(path, true);
} break; } break;
} }
} }

View File

@ -1829,7 +1829,7 @@ void ProjectList::_favorite_pressed(Node *p_hb) {
} }
void ProjectList::_show_project(const String &p_path) { void ProjectList::_show_project(const String &p_path) {
OS::get_singleton()->shell_open(String("file://") + p_path); OS::get_singleton()->shell_show_in_file_manager(p_path, true);
} }
void ProjectList::_bind_methods() { void ProjectList::_bind_methods() {

View File

@ -1351,6 +1351,51 @@ Error OS_Windows::shell_open(String p_uri) {
} }
} }
Error OS_Windows::shell_show_in_file_manager(String p_path, bool p_open_folder) {
p_path = p_path.trim_suffix("file://");
bool open_folder = false;
if (DirAccess::dir_exists_absolute(p_path) && p_open_folder) {
open_folder = true;
}
if (p_path.begins_with("\"")) {
p_path = String("\"") + p_path;
}
if (p_path.ends_with("\"")) {
p_path = p_path + String("\"");
}
p_path = p_path.replace("/", "\\");
INT_PTR ret = OK;
if (open_folder) {
ret = (INT_PTR)ShellExecuteW(nullptr, nullptr, L"explorer.exe", LPCWSTR(p_path.utf16().get_data()), nullptr, SW_SHOWNORMAL);
} else {
ret = (INT_PTR)ShellExecuteW(nullptr, nullptr, L"explorer.exe", LPCWSTR((String("/select,") + p_path).utf16().get_data()), nullptr, SW_SHOWNORMAL);
}
if (ret > 32) {
return OK;
} else {
switch (ret) {
case ERROR_FILE_NOT_FOUND:
case SE_ERR_DLLNOTFOUND:
return ERR_FILE_NOT_FOUND;
case ERROR_PATH_NOT_FOUND:
return ERR_FILE_BAD_PATH;
case ERROR_BAD_FORMAT:
return ERR_FILE_CORRUPT;
case SE_ERR_ACCESSDENIED:
return ERR_UNAUTHORIZED;
case 0:
case SE_ERR_OOM:
return ERR_OUT_OF_MEMORY;
default:
return FAILED;
}
}
}
String OS_Windows::get_locale() const { String OS_Windows::get_locale() const {
const _WinLocale *wl = &_win_locales[0]; const _WinLocale *wl = &_win_locales[0];

View File

@ -214,6 +214,7 @@ public:
virtual String get_unique_id() const override; virtual String get_unique_id() const override;
virtual Error shell_open(String p_uri) override; virtual Error shell_open(String p_uri) override;
virtual Error shell_show_in_file_manager(String p_path, bool p_open_folder) override;
void run(); void run();