Skip to content

Commit

Permalink
Merge pull request #79574 from bruvzg/win_nat_d
Browse files Browse the repository at this point in the history
[Windows] Implement native file selection dialog support.
  • Loading branch information
YuriSizov authored Jul 18, 2023
2 parents 2797325 + d3ca91a commit e8aa8c1
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 6 deletions.
2 changes: 1 addition & 1 deletion doc/classes/DisplayServer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@
Displays OS native dialog for selecting files or directories in the file system.
Callbacks have the following arguments: [code]bool status, PackedStringArray selected_paths[/code].
[b]Note:[/b] This method is implemented if the display server has the [code]FEATURE_NATIVE_DIALOG[/code] feature.
[b]Note:[/b] This method is implemented on macOS.
[b]Note:[/b] This method is implemented on Windows and macOS.
[b]Note:[/b] On macOS, native file dialogs have no title.
[b]Note:[/b] On macOS, sandboxed apps will save security-scoped bookmarks to retain access to the opened folders across multiple sessions. Use [method OS.get_granted_permissions] to get a list of saved bookmarks.
</description>
Expand Down
128 changes: 128 additions & 0 deletions platform/windows/display_server_windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

#include "os_windows.h"

#include "core/config/project_settings.h"
#include "core/io/marshalls.h"
#include "main/main.h"
#include "scene/resources/atlas_texture.h"
Expand All @@ -42,6 +43,9 @@

#include <avrt.h>
#include <dwmapi.h>
#include <shlwapi.h>
#include <shobjidl.h>
#include <shobjidl_core.h>

#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
Expand Down Expand Up @@ -87,6 +91,7 @@ bool DisplayServerWindows::has_feature(Feature p_feature) const {
case FEATURE_HIDPI:
case FEATURE_ICON:
case FEATURE_NATIVE_ICON:
case FEATURE_NATIVE_DIALOG:
case FEATURE_SWAP_BUFFERS:
case FEATURE_KEEP_SCREEN_ON:
case FEATURE_TEXT_TO_SPEECH:
Expand Down Expand Up @@ -213,6 +218,129 @@ void DisplayServerWindows::tts_stop() {
tts->stop();
}

Error DisplayServerWindows::file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) {
_THREAD_SAFE_METHOD_

Vector<Char16String> filter_names;
Vector<Char16String> filter_exts;
for (const String &E : p_filters) {
Vector<String> tokens = E.split(";");
if (tokens.size() == 2) {
filter_exts.push_back(tokens[0].strip_edges().utf16());
filter_names.push_back(tokens[1].strip_edges().utf16());
} else if (tokens.size() == 1) {
filter_exts.push_back(tokens[0].strip_edges().utf16());
filter_names.push_back(tokens[0].strip_edges().utf16());
}
}

Vector<COMDLG_FILTERSPEC> filters;
for (int i = 0; i < filter_names.size(); i++) {
filters.push_back({ (LPCWSTR)filter_names[i].ptr(), (LPCWSTR)filter_exts[i].ptr() });
}

HRESULT hr = S_OK;
IFileDialog *pfd = nullptr;
if (p_mode == FILE_DIALOG_MODE_SAVE_FILE) {
hr = CoCreateInstance(CLSID_FileSaveDialog, nullptr, CLSCTX_INPROC_SERVER, IID_IFileSaveDialog, (void **)&pfd);
} else {
hr = CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_INPROC_SERVER, IID_IFileOpenDialog, (void **)&pfd);
}
if (SUCCEEDED(hr)) {
DWORD flags;
pfd->GetOptions(&flags);
if (p_mode == FILE_DIALOG_MODE_OPEN_FILES) {
flags |= FOS_ALLOWMULTISELECT;
}
if (p_mode == FILE_DIALOG_MODE_OPEN_DIR) {
flags |= FOS_PICKFOLDERS;
}
if (p_show_hidden) {
flags |= FOS_FORCESHOWHIDDEN;
}
pfd->SetOptions(flags | FOS_FORCEFILESYSTEM);
pfd->SetTitle((LPCWSTR)p_title.utf16().ptr());

String dir = ProjectSettings::get_singleton()->globalize_path(p_current_directory);
if (dir == ".") {
dir = OS::get_singleton()->get_executable_path().get_base_dir();
}
dir = dir.replace("/", "\\");

IShellItem *shellitem = nullptr;
hr = SHCreateItemFromParsingName((LPCWSTR)dir.utf16().ptr(), nullptr, IID_IShellItem, (void **)&shellitem);
if (SUCCEEDED(hr)) {
pfd->SetDefaultFolder(shellitem);
pfd->SetFolder(shellitem);
}

pfd->SetFileName((LPCWSTR)p_filename.utf16().ptr());
pfd->SetFileTypes(filters.size(), filters.ptr());
pfd->SetFileTypeIndex(0);

hr = pfd->Show(nullptr);
if (SUCCEEDED(hr)) {
Vector<String> file_names;

if (p_mode == FILE_DIALOG_MODE_OPEN_FILES) {
IShellItemArray *results;
hr = static_cast<IFileOpenDialog *>(pfd)->GetResults(&results);
if (SUCCEEDED(hr)) {
DWORD count = 0;
results->GetCount(&count);
for (DWORD i = 0; i < count; i++) {
IShellItem *result;
results->GetItemAt(i, &result);

PWSTR file_path = nullptr;
hr = result->GetDisplayName(SIGDN_FILESYSPATH, &file_path);
if (SUCCEEDED(hr)) {
file_names.push_back(String::utf16((const char16_t *)file_path));
CoTaskMemFree(file_path);
}
result->Release();
}
results->Release();
}
} else {
IShellItem *result;
hr = pfd->GetResult(&result);
if (SUCCEEDED(hr)) {
PWSTR file_path = nullptr;
hr = result->GetDisplayName(SIGDN_FILESYSPATH, &file_path);
if (SUCCEEDED(hr)) {
file_names.push_back(String::utf16((const char16_t *)file_path));
CoTaskMemFree(file_path);
}
result->Release();
}
}
if (!p_callback.is_null()) {
Variant v_status = true;
Variant v_files = file_names;
Variant *v_args[2] = { &v_status, &v_files };
Variant ret;
Callable::CallError ce;
p_callback.callp((const Variant **)&v_args, 2, ret, ce);
}
} else {
if (!p_callback.is_null()) {
Variant v_status = false;
Variant v_files = Vector<String>();
Variant *v_args[2] = { &v_status, &v_files };
Variant ret;
Callable::CallError ce;
p_callback.callp((const Variant **)&v_args, 2, ret, ce);
}
}
pfd->Release();

return OK;
} else {
return ERR_CANT_OPEN;
}
}

void DisplayServerWindows::mouse_set_mode(MouseMode p_mode) {
_THREAD_SAFE_METHOD_

Expand Down
2 changes: 2 additions & 0 deletions platform/windows/display_server_windows.h
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,8 @@ class DisplayServerWindows : public DisplayServer {
virtual bool is_dark_mode() const override;
virtual Color get_accent_color() const override;

virtual Error file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) override;

virtual void mouse_set_mode(MouseMode p_mode) override;
virtual MouseMode mouse_get_mode() const override;

Expand Down
10 changes: 5 additions & 5 deletions scene/gui/file_dialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,22 +67,22 @@ void FileDialog::_native_dialog_cb(bool p_ok, const Vector<String> &p_files) {
if (p_files.size() > 0) {
String f = p_files[0];
if (mode == FILE_MODE_OPEN_FILES) {
emit_signal("files_selected", p_files);
emit_signal(SNAME("files_selected"), p_files);
} else {
if (mode == FILE_MODE_SAVE_FILE) {
emit_signal("file_selected", f);
emit_signal(SNAME("file_selected"), f);
} else if ((mode == FILE_MODE_OPEN_ANY || mode == FILE_MODE_OPEN_FILE) && dir_access->file_exists(f)) {
emit_signal("file_selected", f);
emit_signal(SNAME("file_selected"), f);
} else if (mode == FILE_MODE_OPEN_ANY || mode == FILE_MODE_OPEN_DIR) {
emit_signal("dir_selected", f);
emit_signal(SNAME("dir_selected"), f);
}
}
file->set_text(f);
dir->set_text(f.get_base_dir());
}
} else {
file->set_text("");
emit_signal("cancelled");
emit_signal(SNAME("canceled"));
}
}

Expand Down

0 comments on commit e8aa8c1

Please sign in to comment.