Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement and expose OS.shell_show_in_file_manager() #69698

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions core/core_bind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,15 @@ Error OS::shell_open(String 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() {
return ::OS::get_singleton()->get_stdin_string();
}
Expand Down Expand Up @@ -549,6 +558,7 @@ void OS::_bind_methods() {
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("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("get_process_id"), &OS::get_process_id);

Expand Down
1 change: 1 addition & 0 deletions core/core_bind.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ class OS : public Object {
int create_instance(const Vector<String> &p_arguments);
Error kill(int p_pid);
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;
int get_process_id() const;
Expand Down
9 changes: 9 additions & 0 deletions core/os/os.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,15 @@ Error OS::shell_open(String p_uri) {
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?

uint64_t OS::get_static_memory_usage() const {
Expand Down
1 change: 1 addition & 0 deletions core/os/os.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ class OS {
virtual void vibrate_handheld(int p_duration_ms = 500) {}

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 bool has_environment(const String &p_var) const = 0;
Expand Down
11 changes: 11 additions & 0 deletions doc/classes/OS.xml
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,17 @@
[b]Note:[/b] This method is implemented on Android, iOS, Web, Linux, macOS and Windows.
</description>
</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">
<return type="void" />
<param index="0" name="variable" type="String" />
Expand Down
10 changes: 5 additions & 5 deletions editor/editor_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2995,10 +2995,10 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
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.
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;
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;
case FILE_QUIT:
case RUN_PROJECT_MANAGER:
Expand Down Expand Up @@ -3070,10 +3070,10 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
editor_settings_dialog->popup_edit_settings();
} break;
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;
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;
case SETTINGS_MANAGE_EXPORT_TEMPLATES: {
export_template_manager->popup_manager();
Expand Down Expand Up @@ -3176,7 +3176,7 @@ void EditorNode::_screenshot(bool p_use_utc) {
NodePath path = String("user://") + name;
_save_screenshot(path);
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);
}
}

Expand Down
2 changes: 1 addition & 1 deletion editor/export/export_template_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
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() {
Expand Down
5 changes: 1 addition & 4 deletions editor/filesystem_dock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1881,11 +1881,8 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected
fpath = p_selected[0];
}

if (!fpath.ends_with("/")) {
fpath = fpath.get_base_dir();
}
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;

case FILE_OPEN_EXTERNAL: {
Expand Down
5 changes: 1 addition & 4 deletions editor/gui/editor_file_dialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Dictionary item_meta = item_list->get_item_metadata(idx);
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;
}
}
Expand Down
2 changes: 1 addition & 1 deletion editor/project_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1827,7 +1827,7 @@ void ProjectList::_favorite_pressed(Node *p_hb) {
}

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() {
Expand Down
45 changes: 45 additions & 0 deletions platform/windows/os_windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1313,6 +1313,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 {
const _WinLocale *wl = &_win_locales[0];

Expand Down
1 change: 1 addition & 0 deletions platform/windows/os_windows.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ class OS_Windows : public OS {
virtual String get_unique_id() const 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();

Expand Down