IO: Add support for multiple drag-n-drop files #107230

Merged
Brecht Van Lommel merged 20 commits from guishe/blender:dragndrop-files into main 2023-12-12 18:46:22 +01:00
14 changed files with 65 additions and 41 deletions
Showing only changes of commit 722b8a3475 - Show all commits

View File

@ -64,7 +64,7 @@ void UI_but_drag_set_path(uiBut *but, const char *path)
if (but->dragflag & UI_BUT_DRAGPOIN_FREE) {
WM_drag_data_free(but->dragtype, but->dragpoin);
}
but->dragpoin = WM_drag_create_path_data(path);
but->dragpoin = WM_drag_create_path_data(blender::Span(&path, 1));
but->dragflag |= UI_BUT_DRAGPOIN_FREE;
}

View File

@ -5907,7 +5907,7 @@ static bool blend_file_drop_poll(bContext * /*C*/, wmDrag *drag, const wmEvent *
static void blend_file_drop_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop)
{
/* copy drag path to properties */
RNA_string_set(drop->ptr, "filepath", WM_drag_get_path(drag));
RNA_string_set(drop->ptr, "filepath", WM_drag_get_paths(drag)[0].c_str());
}
void ED_keymap_screen(wmKeyConfig *keyconf)

View File

@ -536,7 +536,8 @@ static void clip_drop_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop)
PointerRNA itemptr;
char dir[FILE_MAX], file[FILE_MAX];
BLI_path_split_dir_file(WM_drag_get_path(drag), dir, sizeof(dir), file, sizeof(file));
BLI_path_split_dir_file(
WM_drag_get_paths(drag)[0].c_str(), dir, sizeof(dir), file, sizeof(file));
RNA_string_set(drop->ptr, "directory", dir);

View File

@ -174,7 +174,7 @@ static bool path_drop_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*eve
static void path_drop_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop)
{
char pathname[FILE_MAX + 2];
SNPRINTF(pathname, "\"%s\"", WM_drag_get_path(drag));
SNPRINTF(pathname, "\"%s\"", WM_drag_get_paths(drag)[0].c_str());
RNA_string_set(drop->ptr, "text", pathname);
}

View File

@ -799,7 +799,7 @@ static bool filepath_drop_poll(bContext *C, wmDrag *drag, const wmEvent * /*even
static void filepath_drop_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop)
{
RNA_string_set(drop->ptr, "filepath", WM_drag_get_path(drag));
RNA_string_set(drop->ptr, "filepath", WM_drag_get_paths(drag)[0].c_str());
}
/* region dropbox definition */

View File

@ -268,7 +268,7 @@ static bool image_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
static void image_drop_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop)
{
/* copy drag path to properties */
RNA_string_set(drop->ptr, "filepath", WM_drag_get_path(drag));
RNA_string_set(drop->ptr, "filepath", WM_drag_get_paths(drag)[0].c_str());
}
/* area+region dropbox definition */

View File

@ -725,9 +725,9 @@ static void node_id_path_drop_copy(bContext *C, wmDrag *drag, wmDropBox *drop)
return;
}
const char *path = WM_drag_get_path(drag);
if (path) {
RNA_string_set(drop->ptr, "filepath", path);
const auto paths = WM_drag_get_paths(drag);
if (paths.begin()) {
RNA_string_set(drop->ptr, "filepath", paths[0].c_str());
RNA_struct_property_unset(drop->ptr, "name");
return;
}

View File

@ -249,17 +249,17 @@ static void sequencer_drop_copy(bContext *C, wmDrag *drag, wmDropBox *drop)
return;
}
const char *path = WM_drag_get_path(drag);
const auto paths = WM_drag_get_paths(drag);
/* Path dropped. */
if (path) {
if (paths.begin()) {
if (RNA_struct_find_property(drop->ptr, "filepath")) {
RNA_string_set(drop->ptr, "filepath", path);
RNA_string_set(drop->ptr, "filepath", paths[0].c_str());
}
if (RNA_struct_find_property(drop->ptr, "directory")) {
PointerRNA itemptr;
char dir[FILE_MAX], file[FILE_MAX];
BLI_path_split_dir_file(path, dir, sizeof(dir), file, sizeof(file));
BLI_path_split_dir_file(paths[0].c_str(), dir, sizeof(dir), file, sizeof(file));
RNA_string_set(drop->ptr, "directory", dir);
@ -337,7 +337,7 @@ static void get_drag_path(const bContext *C, wmDrag *drag, char r_path[FILE_MAX]
BLI_path_abs(r_path, BKE_main_blendfile_path_from_global());
}
else {
BLI_strncpy(r_path, WM_drag_get_path(drag), FILE_MAX);
BLI_strncpy(r_path, WM_drag_get_paths(drag)[0].c_str(), FILE_MAX);
}
}

View File

@ -315,7 +315,7 @@ static bool text_drop_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*eve
static void text_drop_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop)
{
/* copy drag path to properties */
RNA_string_set(drop->ptr, "filepath", WM_drag_get_path(drag));
RNA_string_set(drop->ptr, "filepath", WM_drag_get_paths(drag)[0].c_str());
}
static bool text_drop_paste_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*event*/)

View File

@ -927,9 +927,9 @@ static void view3d_id_path_drop_copy(bContext *C, wmDrag *drag, wmDropBox *drop)
RNA_struct_property_unset(drop->ptr, "filepath");
return;
}
const char *path = WM_drag_get_path(drag);
if (path) {
RNA_string_set(drop->ptr, "filepath", path);
const auto paths = WM_drag_get_paths(drag);
if (paths.begin()) {
RNA_string_set(drop->ptr, "filepath", paths[0].c_str());
RNA_struct_property_unset(drop->ptr, "image");
}
}

View File

@ -1393,13 +1393,13 @@ const ListBase *WM_drag_asset_list_get(const wmDrag *drag);
const char *WM_drag_get_item_name(wmDrag *drag);
/* Path drag and drop. */
/* Paths drag and drop. */
/**
* \param path: The path to drag. Value will be copied into the drag data so the passed string may
* be destructed.
* \param paths: The paths to drag. Values will be copied into the drag data so the passed strings
* may be destructed. Only paths that share the same extension of the first file will be copied.
*/
wmDragPath *WM_drag_create_path_data(const char *path);
const char *WM_drag_get_path(const wmDrag *drag);
wmDragPath *WM_drag_create_path_data(blender::Span<const char *> paths);
const blender::Span<std::string> WM_drag_get_paths(const wmDrag *drag);
/**
* Note that even though the enum return type uses bit-flags, this should never have multiple
* type-bits set, so `ELEM()` like comparison is possible.

View File

@ -106,6 +106,7 @@ struct wmWindowManager;
#include "BLI_compiler_attrs.h"
#include "BLI_utildefines.h"
#include "BLI_vector.hh"
#include "DNA_listBase.h"
#include "DNA_uuid_types.h"
#include "DNA_vec_types.h"
@ -1125,10 +1126,15 @@ struct wmDragAssetListItem {
};
struct wmDragPath {
char *path;
const blender::Vector<std::string> paths;
/* Note that even though the enum type uses bit-flags, this should never have multiple type-bits
* set, so `ELEM()` like comparison is possible. */
int file_type; /* eFileSel_File_Types */
const int file_type; /* eFileSel_File_Types */
const std::string tooltip;
wmDragPath(const blender::Vector<std::string> &paths, const std::string &tooltip, int file_type)
: paths(std::move(paths)), file_type(file_type), tooltip(tooltip)
{
}
};
struct wmDragGreasePencilLayer {

View File

@ -760,29 +760,45 @@ const ListBase *WM_drag_asset_list_get(const wmDrag *drag)
return &drag->asset_items;
}
wmDragPath *WM_drag_create_path_data(const char *path)
wmDragPath *WM_drag_create_path_data(blender::Span<const char *> _paths)
{
wmDragPath *path_data = MEM_new<wmDragPath>("wmDragPath");
path_data->path = BLI_strdup(path);
path_data->file_type = ED_path_extension_type(path);
const char *ext = BLI_path_extension(_paths[0]);
blender::Vector<std::string> paths;
for (auto path : _paths) {
if (STREQ(ext, BLI_path_extension(path))) {
brecht marked this conversation as resolved Outdated

Is there a reason for using string extension instead of the (already computed) file_type here (and then comparing to the result of ED_path_extension_type for all other paths in the loop below)?

If yes, should be documented in a comment.

Current approach would also 'fail' (filter out valid paths) e.g. in case of paths to a mix of JPG and PNG images. Or paths containing several USD files with different extensions.

Is there a reason for using string extension instead of the (already computed) `file_type` here (and then comparing to the result of `ED_path_extension_type` for all other paths in the loop below)? If yes, should be documented in a comment. Current approach would also 'fail' (filter out valid paths) e.g. in case of paths to a mix of JPG and PNG images. Or paths containing several USD files with different extensions.

ED_path_extension_type is only for file types that Blender natively supports, while this system is meant to work for arbitrary add-ons.

`ED_path_extension_type` is only for file types that Blender natively supports, while this system is meant to work for arbitrary add-ons.

Initially I had the idea of just the one extension for file handler, but since file handlers could have a list of files they can take, here we can copy all and then see which ones are useful for the file handlers.

path_data->file_type would only be used internally with the first file, and since these changes don't change the behavior I think it's safe to keep it and collect all the file paths, I'll leave a note that this file_type will just you describe the first path.

Initially I had the idea of just the one extension for file handler, but since file handlers could have a list of files they can take, here we can copy all and then see which ones are useful for the file handlers. `path_data->file_type` would only be used internally with the first file, and since these changes don't change the behavior I think it's safe to keep it and collect all the file paths, I'll leave a note that this `file_type` will just you describe the first path.

Ah yes, makes sense, better not do any filtering here then indeed.

Ah yes, makes sense, better not do any filtering here then indeed.
paths.append(path);
}
guishe marked this conversation as resolved Outdated

This is just const char* isn't it? In that case auto just hides type information with no real benefit, prefer not using it in such cases.

This is just `const char*` isn't it? In that case `auto` just hides type information with no real benefit, prefer not using it in such cases.
}
char *tooltip;
if (_paths.size() == 1) {
tooltip = BLI_strdup(_paths[0]);
}
else {
tooltip = BLI_sprintfN(TIP_("Dragging %d %s files."), paths.size());
guishe marked this conversation as resolved Outdated

There is no reason to use C-strings here. std::string and fmt::format (extern library we are using since we are not yet on C++20) should be even easier to use.

There is no reason to use C-strings here. `std::string` and `fmt::format` (extern library we are using since we are not yet on C++20) should be even easier to use.
Review

No final point in our UI strings: "Dragging {} files"

No final point in our UI strings: `"Dragging {} files"`
}
wmDragPath *path_data = MEM_new<wmDragPath>(
"wmDragPath", paths, tooltip, ED_path_extension_type(_paths[0]));
MEM_freeN(tooltip);
return path_data;
}
static void wm_drag_free_path_data(wmDragPath **path_data)
{
MEM_freeN((*path_data)->path);
MEM_delete(*path_data);
*path_data = nullptr;
}
const char *WM_drag_get_path(const wmDrag *drag)
const blender::Span<std::string> WM_drag_get_paths(const wmDrag *drag)
{
if (drag->type != WM_DRAG_PATH) {
return nullptr;
return blender::Span<std::string>();
}
const wmDragPath *path_data = static_cast<const wmDragPath *>(drag->poin);
guishe marked this conversation as resolved Outdated

Same, remove const from return type.

Same, remove `const` from return type.
return path_data->path;
return path_data->paths.as_span();
}
guishe marked this conversation as resolved Outdated

return nullptr;

`return nullptr;`
int WM_drag_get_path_file_type(const wmDrag *drag)
@ -846,7 +862,7 @@ const char *WM_drag_get_item_name(wmDrag *drag)
}
case WM_DRAG_PATH: {
const wmDragPath *path_drag_data = static_cast<const wmDragPath *>(drag->poin);
return path_drag_data->path;
return path_drag_data->tooltip.c_str();
}
case WM_DRAG_NAME:
return static_cast<const char *>(drag->poin);
@ -1039,8 +1055,9 @@ void wm_drags_draw(bContext *C, wmWindow *win)
wmWindowViewport(win);
}
/* Drawing should be allowed to assume the context from handling and polling (that's why we
* restore it above). */
/* Drawing should be allowed to assume the context from
* handling and polling (that's why we restore it
* above). */
if (drag->drop_state.active_dropbox->draw_droptip) {
drag->drop_state.active_dropbox->draw_droptip(C, win, drag, xy);
continue;

View File

@ -1512,14 +1512,14 @@ wm_window_swap_buffers(win);
if (ddd->dataType == GHOST_kDragnDropTypeFilenames) {
GHOST_TStringArray *stra = static_cast<GHOST_TStringArray *>(ddd->data);
for (int a = 0; a < stra->count; a++) {
printf("drop file %s\n", stra->strings[a]);
if (stra->count) {
printf("drop file %s\n", stra->strings[0]);
/* try to get icon type from extension */
int icon = ED_file_extension_icon((char *)stra->strings[a]);
wmDragPath *path_data = WM_drag_create_path_data((char *)stra->strings[a]);
int icon = ED_file_extension_icon((char *)stra->strings[0]);
wmDragPath *path_data = WM_drag_create_path_data(
blender::Span((char **)stra->strings, stra->count));
WM_event_start_drag(C, icon, WM_DRAG_PATH, path_data, 0.0, WM_DRAG_NOP);
/* void poin should point to string, it makes a copy */
break; /* only one drop element supported now */
}
}