IO: Add support for multiple drag-n-drop files #107230
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -5944,7 +5944,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_single_path(drag));
|
||||
}
|
||||
|
||||
void ED_keymap_screen(wmKeyConfig *keyconf)
|
||||
|
|
|
@ -536,7 +536,7 @@ 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_single_path(drag), dir, sizeof(dir), file, sizeof(file));
|
||||
|
||||
RNA_string_set(drop->ptr, "directory", dir);
|
||||
|
||||
|
|
|
@ -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_single_path(drag));
|
||||
RNA_string_set(drop->ptr, "text", pathname);
|
||||
}
|
||||
|
||||
|
|
|
@ -797,7 +797,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_single_path(drag));
|
||||
}
|
||||
|
||||
/* region dropbox definition */
|
||||
|
|
|
@ -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_single_path(drag));
|
||||
}
|
||||
|
||||
/* area+region dropbox definition */
|
||||
|
|
|
@ -897,7 +897,7 @@ static void node_id_path_drop_copy(bContext *C, wmDrag *drag, wmDropBox *drop)
|
|||
return;
|
||||
}
|
||||
|
||||
const char *path = WM_drag_get_path(drag);
|
||||
const char *path = WM_drag_get_single_path(drag);
|
||||
if (path) {
|
||||
RNA_string_set(drop->ptr, "filepath", path);
|
||||
RNA_struct_property_unset(drop->ptr, "name");
|
||||
|
|
|
@ -249,7 +249,7 @@ static void sequencer_drop_copy(bContext *C, wmDrag *drag, wmDropBox *drop)
|
|||
return;
|
||||
}
|
||||
|
||||
const char *path = WM_drag_get_path(drag);
|
||||
const char *path = WM_drag_get_single_path(drag);
|
||||
/* Path dropped. */
|
||||
if (path) {
|
||||
if (RNA_struct_find_property(drop->ptr, "filepath")) {
|
||||
|
@ -335,7 +335,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_single_path(drag), FILE_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -316,7 +316,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_single_path(drag));
|
||||
}
|
||||
|
||||
static bool text_drop_paste_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*event*/)
|
||||
|
|
|
@ -893,7 +893,7 @@ 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);
|
||||
const char *path = WM_drag_get_single_path(drag);
|
||||
if (path) {
|
||||
RNA_string_set(drop->ptr, "filepath", path);
|
||||
RNA_struct_property_unset(drop->ptr, "image");
|
||||
|
|
|
@ -210,3 +210,11 @@ blender_add_lib_nolist(bf_windowmanager "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
|||
|
||||
# RNA_prototypes.h
|
||||
add_dependencies(bf_windowmanager bf_rna)
|
||||
|
||||
if(WITH_GTESTS)
|
||||
set(TEST_SRC
|
||||
intern/wm_dragdrop_test.cc
|
||||
)
|
||||
include(GTestTesting)
|
||||
blender_add_test_lib(bf_wm_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB}")
|
||||
endif()
|
||||
|
|
|
@ -1424,16 +1424,32 @@ 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.
|
||||
*/
|
||||
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);
|
||||
/* If #drag contains path data, returns the first path int he path list. */
|
||||
const char *WM_drag_get_single_path(const wmDrag *drag);
|
||||
/* If #drag contains path data, returns the first path in the path list that maches a
|
||||
* a `file_type`.*/
|
||||
/*
|
||||
* \param drag: The drag that could contain drag path data.
|
||||
* \param file_type: `eFileSel_File_Types` bit flag
|
||||
*/
|
||||
const char *WM_drag_get_single_path(const wmDrag *drag, int file_type);
|
||||
blender::Span<std::string> WM_drag_get_paths(const wmDrag *drag);
|
||||
/* If #drag contains path data, returns if any file path match a `file_type`.*/
|
||||
/*
|
||||
* \param drag: The drag that could contain drag path data.
|
||||
* \param file_type: `eFileSel_File_Types` bit flag
|
||||
*/
|
||||
bool WM_drag_has_path_file_type(const wmDrag *drag, int file_type);
|
||||
/**
|
||||
* 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.
|
||||
* type-bits set, so `ELEM()` like comparison is possible. To check all paths or to do a bit-flag
|
||||
* check use `WM_drag_has_path_file_type(drag,file_type)` instead.
|
||||
*/
|
||||
int /* eFileSel_File_Types */ WM_drag_get_path_file_type(const wmDrag *drag);
|
||||
|
||||
|
|
|
@ -111,6 +111,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"
|
||||
|
@ -1179,10 +1180,12 @@ struct wmDragAssetListItem {
|
|||
};
|
||||
|
||||
struct wmDragPath {
|
||||
char *path;
|
||||
/* 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 */
|
||||
blender::Vector<std::string> paths;
|
||||
/* File type of each path in #paths. */
|
||||
blender::Vector<int> file_types; /* eFileSel_File_Types */
|
||||
/* Bit flag of file types in #paths. */
|
||||
int file_types_bit_flag; /* eFileSel_File_Types */
|
||||
std::string tooltip;
|
||||
};
|
||||
|
||||
struct wmDragGreasePencilLayer {
|
||||
|
|
|
@ -60,6 +60,7 @@
|
|||
#include "wm_event_system.h"
|
||||
#include "wm_window.hh"
|
||||
|
||||
#include <fmt/format.h>
|
||||
/* ****************************************************** */
|
||||
|
||||
static ListBase dropboxes = {nullptr, nullptr};
|
||||
|
@ -762,29 +763,80 @@ 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)
|
||||
{
|
||||
BLI_assert(!paths.is_empty());
|
||||
wmDragPath *path_data = MEM_new<wmDragPath>("wmDragPath");
|
||||
path_data->path = BLI_strdup(path);
|
||||
path_data->file_type = ED_path_extension_type(path);
|
||||
|
||||
for (const char *path : paths) {
|
||||
path_data->paths.append(path);
|
||||
path_data->file_types_bit_flag |= ED_path_extension_type(path);
|
||||
path_data->file_types.append(ED_path_extension_type(path));
|
||||
}
|
||||
|
||||
path_data->tooltip = path_data->paths[0];
|
||||
guishe marked this conversation as resolved
|
||||
|
||||
if (path_data->paths.size() > 1) {
|
||||
std::string path_count = std::to_string(path_data->paths.size());
|
||||
path_data->tooltip = fmt::format(TIP_("Dragging {} files"), path_count);
|
||||
}
|
||||
|
||||
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 char *WM_drag_get_single_path(const wmDrag *drag)
|
||||
{
|
||||
if (drag->type != WM_DRAG_PATH) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const wmDragPath *path_data = static_cast<const wmDragPath *>(drag->poin);
|
||||
return path_data->path;
|
||||
return path_data->paths[0].c_str();
|
||||
}
|
||||
|
||||
const char *WM_drag_get_single_path(const wmDrag *drag, int file_type)
|
||||
{
|
||||
if (drag->type != WM_DRAG_PATH) {
|
||||
return nullptr;
|
||||
}
|
||||
const wmDragPath *path_data = static_cast<const wmDragPath *>(drag->poin);
|
||||
auto const file_types = path_data->file_types;
|
||||
|
||||
auto itr = std::find_if(
|
||||
file_types.begin(), file_types.end(), [file_type](const int file_fype_test) {
|
||||
return file_fype_test & file_type;
|
||||
});
|
||||
|
||||
if (itr == file_types.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
const int index = itr - file_types.begin();
|
||||
return path_data->paths[index].c_str();
|
||||
}
|
||||
|
||||
bool WM_drag_has_path_file_type(const wmDrag *drag, int file_type)
|
||||
{
|
||||
if (drag->type != WM_DRAG_PATH) {
|
||||
return false;
|
||||
}
|
||||
const wmDragPath *path_data = static_cast<const wmDragPath *>(drag->poin);
|
||||
return bool(path_data->file_types_bit_flag & file_type);
|
||||
}
|
||||
|
||||
blender::Span<std::string> WM_drag_get_paths(const wmDrag *drag)
|
||||
{
|
||||
if (drag->type != WM_DRAG_PATH) {
|
||||
return blender::Span<std::string>();
|
||||
}
|
||||
|
||||
const wmDragPath *path_data = static_cast<const wmDragPath *>(drag->poin);
|
||||
return path_data->paths.as_span();
|
||||
}
|
||||
|
||||
int WM_drag_get_path_file_type(const wmDrag *drag)
|
||||
|
@ -794,7 +846,7 @@ int WM_drag_get_path_file_type(const wmDrag *drag)
|
|||
}
|
||||
|
||||
const wmDragPath *path_data = static_cast<const wmDragPath *>(drag->poin);
|
||||
return path_data->file_type;
|
||||
return path_data->file_types[0];
|
||||
}
|
||||
|
||||
/* ************** draw ***************** */
|
||||
|
@ -848,7 +900,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);
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "testing/testing.h"
|
||||
|
||||
/* #eFileSel_File_Types. */
|
||||
#include "DNA_space_types.h"
|
||||
brecht marked this conversation as resolved
Julian Eisel
commented
Doesn't seem to be needed. Doesn't seem to be needed.
Guillermo Venegas
commented
Needed by Needed by `wmDrag.drop_state.ui_context`, is a `std::unique_ptr<bContextStore>`
Julian Eisel
commented
While not a big deal, this include shouldn't be necessary. Committed While not a big deal, this include shouldn't be necessary. Committed e3b3399bcb so this can be removed now.
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
namespace blender::tests {
|
||||
|
||||
TEST(wm_drag, wmDragPath)
|
||||
{
|
||||
{
|
||||
/**
|
||||
* NOTE: `WM_drag_create_path_data` gets the `file_type` from the first path in `paths` and
|
||||
* only needs its extension, so there is no need to describe a full path here that can have a
|
||||
* different format on Windows or Linux. However callers must ensure that they are valid paths.
|
||||
*/
|
||||
blender::Vector<const char *> paths{"text_file.txt"};
|
||||
wmDragPath *path_data = WM_drag_create_path_data(paths);
|
||||
blender::Vector<std::string> expected_file_paths{"text_file.txt"};
|
||||
|
||||
EXPECT_EQ(path_data->paths.size(), 1);
|
||||
EXPECT_EQ(path_data->tooltip, "text_file.txt");
|
||||
EXPECT_EQ(path_data->paths, expected_file_paths);
|
||||
|
||||
/** Test `wmDrag` path data getters. */
|
||||
wmDrag drag;
|
||||
drag.type = WM_DRAG_PATH;
|
||||
drag.poin = path_data;
|
||||
EXPECT_STREQ(WM_drag_get_single_path(&drag), "text_file.txt");
|
||||
EXPECT_EQ(WM_drag_get_path_file_type(&drag), FILE_TYPE_TEXT);
|
||||
EXPECT_EQ(WM_drag_get_paths(&drag), expected_file_paths.as_span());
|
||||
EXPECT_STREQ(WM_drag_get_single_path(&drag, FILE_TYPE_TEXT), "text_file.txt");
|
||||
EXPECT_EQ(WM_drag_get_single_path(&drag, FILE_TYPE_BLENDER), nullptr);
|
||||
EXPECT_TRUE(
|
||||
WM_drag_has_path_file_type(&drag, FILE_TYPE_BLENDER | FILE_TYPE_TEXT | FILE_TYPE_IMAGE));
|
||||
EXPECT_FALSE(WM_drag_has_path_file_type(&drag, FILE_TYPE_BLENDER | FILE_TYPE_IMAGE));
|
||||
MEM_delete(path_data);
|
||||
}
|
||||
{
|
||||
blender::Vector<const char *> paths = {"blender.blend", "text_file.txt", "image.png"};
|
||||
wmDragPath *path_data = WM_drag_create_path_data(paths);
|
||||
blender::Vector<std::string> expected_file_paths = {
|
||||
"blender.blend", "text_file.txt", "image.png"};
|
||||
|
||||
EXPECT_EQ(path_data->paths.size(), 3);
|
||||
EXPECT_EQ(path_data->tooltip, "Dragging 3 files");
|
||||
EXPECT_EQ(path_data->paths, expected_file_paths);
|
||||
|
||||
/** Test `wmDrag` path data getters. */
|
||||
wmDrag drag;
|
||||
drag.type = WM_DRAG_PATH;
|
||||
drag.poin = path_data;
|
||||
EXPECT_STREQ(WM_drag_get_single_path(&drag), "blender.blend");
|
||||
EXPECT_EQ(WM_drag_get_path_file_type(&drag), FILE_TYPE_BLENDER);
|
||||
EXPECT_EQ(WM_drag_get_paths(&drag), expected_file_paths.as_span());
|
||||
EXPECT_STREQ(WM_drag_get_single_path(&drag, FILE_TYPE_BLENDER), "blender.blend");
|
||||
EXPECT_STREQ(WM_drag_get_single_path(&drag, FILE_TYPE_IMAGE), "image.png");
|
||||
EXPECT_STREQ(WM_drag_get_single_path(&drag, FILE_TYPE_TEXT), "text_file.txt");
|
||||
EXPECT_STREQ(
|
||||
WM_drag_get_single_path(&drag, FILE_TYPE_BLENDER | FILE_TYPE_TEXT | FILE_TYPE_IMAGE),
|
||||
"blender.blend");
|
||||
EXPECT_STREQ(WM_drag_get_single_path(&drag, FILE_TYPE_TEXT | FILE_TYPE_IMAGE),
|
||||
"text_file.txt");
|
||||
EXPECT_EQ(WM_drag_get_single_path(&drag, FILE_TYPE_ASSET), nullptr);
|
||||
EXPECT_TRUE(
|
||||
WM_drag_has_path_file_type(&drag, FILE_TYPE_BLENDER | FILE_TYPE_TEXT | FILE_TYPE_IMAGE));
|
||||
EXPECT_TRUE(WM_drag_has_path_file_type(&drag, FILE_TYPE_BLENDER | FILE_TYPE_IMAGE));
|
||||
EXPECT_TRUE(WM_drag_has_path_file_type(&drag, FILE_TYPE_IMAGE));
|
||||
EXPECT_FALSE(WM_drag_has_path_file_type(&drag, FILE_TYPE_ASSET));
|
||||
MEM_delete(path_data);
|
||||
}
|
||||
{
|
||||
/** Test `wmDrag` path data getters when the drag type is different to `WM_DRAG_PATH`. */
|
||||
wmDrag drag;
|
||||
drag.type = WM_DRAG_COLOR;
|
||||
EXPECT_EQ(WM_drag_get_single_path(&drag), nullptr);
|
||||
EXPECT_EQ(WM_drag_get_path_file_type(&drag), 0);
|
||||
EXPECT_EQ(WM_drag_get_paths(&drag).size(), 0);
|
||||
EXPECT_EQ(WM_drag_get_single_path(
|
||||
&drag, FILE_TYPE_BLENDER | FILE_TYPE_IMAGE | FILE_TYPE_TEXT | FILE_TYPE_ASSET),
|
||||
nullptr);
|
||||
EXPECT_FALSE(WM_drag_has_path_file_type(
|
||||
&drag, FILE_TYPE_BLENDER | FILE_TYPE_IMAGE | FILE_TYPE_TEXT | FILE_TYPE_ASSET));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::tests
|
|
@ -15,6 +15,8 @@
|
|||
#include <cstring>
|
||||
#include <thread>
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
#include "DNA_listBase.h"
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_windowmanager_types.h"
|
||||
|
@ -1649,14 +1651,17 @@ static bool ghost_event_proc(GHOST_EventHandle ghost_event, GHOST_TUserDataPtr C
|
|||
if (ddd->dataType == GHOST_kDragnDropTypeFilenames) {
|
||||
const GHOST_TStringArray *stra = static_cast<const GHOST_TStringArray *>(ddd->data);
|
||||
|
||||
for (int a = 0; a < stra->count; a++) {
|
||||
printf("drop file %s\n", stra->strings[a]);
|
||||
/* 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]);
|
||||
if (stra->count) {
|
||||
CLOG_INFO(WM_LOG_EVENTS, 1, "Drop %d files:", stra->count);
|
||||
for (const char *path : blender::Span((char **)stra->strings, stra->count)) {
|
||||
CLOG_INFO(WM_LOG_EVENTS, 1, "%s", path);
|
||||
}
|
||||
/* Try to get icon type from extension of the first path. */
|
||||
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 pointer should point to string, it makes a copy. */
|
||||
break; /* only one drop element supported now */
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
No final point in our UI strings:
"Dragging {} files"