WIP: UI: Add Drag & Drop Feedback on Windows #107056

Draft
Guillermo Venegas wants to merge 3 commits from guishe/blender:drag-and-drop-update into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
25 changed files with 560 additions and 299 deletions

View File

@ -21,4 +21,8 @@ add_definitions(
-DHAVE_SYS_TIME_H
)
if(WITH_INPUT_NDOF)
add_definitions(-DWITH_INPUT_NDOF)
endif()
blender_add_lib(extern_xdnd "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")

432
extern/xdnd/xdnd.c vendored
View File

@ -627,7 +627,7 @@ static void xdnd_send_status (DndClass * dnd, Window window, Window from, int wi
xevent.xany.display = dnd->display;
xevent.xclient.window = window;
xevent.xclient.message_type = dnd->XdndStatus;
xevent.xclient.format = 32;
xevent.xclient.format = 32;
XDND_STATUS_TARGET_WIN (&xevent) = from;
XDND_STATUS_WILL_ACCEPT_SET (&xevent, will_accept);
@ -1254,183 +1254,6 @@ Atom xdnd_drag (DndClass * dnd, Window from, Atom action, Atom * typelist)
return result;
}
/* returns non-zero if event is handled */
int xdnd_handle_drop_events (DndClass * dnd, XEvent * xevent)
{
int result = 0;
if (xevent->type == SelectionNotify) {
dnd_debug1 ("got SelectionNotify");
if (xevent->xselection.property == dnd->Xdnd_NON_PROTOCOL_ATOM && dnd->stage == XDND_DROP_STAGE_CONVERTING) {
int error;
dnd_debug1 (" property is Xdnd_NON_PROTOCOL_ATOM - getting selection");
error = xdnd_get_selection (dnd, dnd->dragger_window, xevent->xselection.property, xevent->xany.window);
/* error is not actually used, i think future versions of the protocol maybe should return
an error status to the calling window with the XdndFinished client message */
if (dnd_version_at_least (dnd->dragging_version, 2)) {
#if XDND_VERSION >= 3
xdnd_send_finished (dnd, dnd->dragger_window, dnd->dropper_toplevel, error);
#else
xdnd_send_finished (dnd, dnd->dragger_window, dnd->dropper_window, error);
#endif
dnd_debug1 (" sending finished");
}
xdnd_xfree (dnd->dragger_typelist);
xdnd_reset (dnd);
dnd->stage = XDND_DROP_STAGE_IDLE;
result = 1;
} else {
dnd_debug1 (" property is not Xdnd_NON_PROTOCOL_ATOM - ignoring");
}
} else if (xevent->type == ClientMessage) {
dnd_debug2 ("got ClientMessage to xevent->xany.window = %ld", xevent->xany.window);
if (xevent->xclient.message_type == dnd->XdndEnter) {
dnd_debug2 (" message_type is XdndEnter, version = %ld", XDND_ENTER_VERSION (xevent));
#if XDND_VERSION >= 3
if (XDND_ENTER_VERSION (xevent) < 3)
return 0;
#endif
xdnd_reset (dnd);
dnd->dragger_window = XDND_ENTER_SOURCE_WIN (xevent);
#if XDND_VERSION >= 3
dnd->dropper_toplevel = xevent->xany.window;
dnd->dropper_window = 0; /* enter goes to the top level window only,
so we don't really know what the
sub window is yet */
#else
dnd->dropper_window = xevent->xany.window;
#endif
xdnd_xfree (dnd->dragger_typelist);
if (XDND_ENTER_THREE_TYPES (xevent)) {
dnd_debug1 (" three types only");
xdnd_get_three_types (dnd, xevent, &dnd->dragger_typelist);
} else {
dnd_debug1 (" more than three types - getting list");
xdnd_get_type_list (dnd, dnd->dragger_window, &dnd->dragger_typelist);
}
if (dnd->dragger_typelist)
dnd->stage = XDND_DROP_STAGE_ENTERED;
else
dnd_debug1 (" typelist returned as zero!");
dnd->dragging_version = XDND_ENTER_VERSION (xevent);
result = 1;
} else if (xevent->xclient.message_type == dnd->XdndLeave) {
#if XDND_VERSION >= 3
if (xevent->xany.window == dnd->dropper_toplevel && dnd->dropper_window)
xevent->xany.window = dnd->dropper_window;
#endif
dnd_debug1 (" message_type is XdndLeave");
if (dnd->dragger_window == XDND_LEAVE_SOURCE_WIN (xevent) && dnd->stage == XDND_DROP_STAGE_ENTERED) {
dnd_debug1 (" leaving");
if (dnd->widget_apply_leave)
(*dnd->widget_apply_leave) (dnd, xevent->xany.window);
dnd->stage = XDND_DROP_STAGE_IDLE;
xdnd_xfree (dnd->dragger_typelist);
result = 1;
dnd->dropper_toplevel = dnd->dropper_window = 0;
} else {
dnd_debug1 (" wrong stage or from wrong window");
}
} else if (xevent->xclient.message_type == dnd->XdndPosition) {
dnd_debug2 (" message_type is XdndPosition to %ld", xevent->xany.window);
if (dnd->dragger_window == XDND_POSITION_SOURCE_WIN (xevent) && dnd->stage == XDND_DROP_STAGE_ENTERED) {
int want_position;
Atom action;
XRectangle rectangle;
Window last_window;
last_window = dnd->dropper_window;
#if XDND_VERSION >= 3
/* version 3 gives us the top-level window only. WE have to find the child that the pointer is over: */
if (1 || xevent->xany.window != dnd->dropper_toplevel || !dnd->dropper_window) {
Window parent, child, new_child = 0;
dnd->dropper_toplevel = xevent->xany.window;
parent = dnd->root_window;
child = dnd->dropper_toplevel;
for (;;) {
int xd, yd;
new_child = 0;
if (!XTranslateCoordinates (dnd->display, parent, child,
XDND_POSITION_ROOT_X (xevent), XDND_POSITION_ROOT_Y (xevent),
&xd, &yd, &new_child))
break;
if (!new_child)
break;
child = new_child;
}
dnd->dropper_window = xevent->xany.window = child;
dnd_debug2 (" child window translates to %ld", dnd->dropper_window);
} else if (xevent->xany.window == dnd->dropper_toplevel && dnd->dropper_window) {
xevent->xany.window = dnd->dropper_window;
dnd_debug2 (" child window previously found: %ld", dnd->dropper_window);
}
#endif
action = dnd->XdndActionCopy;
dnd->supported_action = dnd->XdndActionCopy;
dnd->x = XDND_POSITION_ROOT_X (xevent);
dnd->y = XDND_POSITION_ROOT_Y (xevent);
dnd->time = CurrentTime;
if (dnd_version_at_least (dnd->dragging_version, 1))
dnd->time = XDND_POSITION_TIME (xevent);
if (dnd_version_at_least (dnd->dragging_version, 1))
action = XDND_POSITION_ACTION (xevent);
#if XDND_VERSION >= 3
if (last_window && last_window != xevent->xany.window)
if (dnd->widget_apply_leave)
(*dnd->widget_apply_leave) (dnd, last_window);
#endif
dnd->will_accept = (*dnd->widget_apply_position) (dnd, xevent->xany.window, dnd->dragger_window,
action, dnd->x, dnd->y, dnd->time, dnd->dragger_typelist,
&want_position, &dnd->supported_action, &dnd->desired_type, &rectangle);
dnd_debug2 (" will accept = %d", dnd->will_accept);
#if XDND_VERSION >= 3
dnd_debug2 (" sending status of %ld", dnd->dropper_toplevel);
xdnd_send_status (dnd, dnd->dragger_window, dnd->dropper_toplevel, dnd->will_accept,
want_position, rectangle.x, rectangle.y, rectangle.width, rectangle.height, dnd->supported_action);
#else
dnd_debug2 (" sending status of %ld", xevent->xany.window);
xdnd_send_status (dnd, dnd->dragger_window, xevent->xany.window, dnd->will_accept,
want_position, rectangle.x, rectangle.y, rectangle.width, rectangle.height, dnd->supported_action);
#endif
result = 1;
} else {
dnd_debug1 (" wrong stage or from wrong window");
}
} else if (xevent->xclient.message_type == dnd->XdndDrop) {
#if XDND_VERSION >= 3
if (xevent->xany.window == dnd->dropper_toplevel && dnd->dropper_window)
xevent->xany.window = dnd->dropper_window;
#endif
dnd_debug1 (" message_type is XdndDrop");
if (dnd->dragger_window == XDND_DROP_SOURCE_WIN (xevent) && dnd->stage == XDND_DROP_STAGE_ENTERED) {
dnd->time = CurrentTime;
if (dnd_version_at_least (dnd->dragging_version, 1))
dnd->time = XDND_DROP_TIME (xevent);
if (dnd->will_accept) {
dnd_debug1 (" will_accept is true - converting selectiong");
dnd_debug2 (" my window is %ld", dnd->dropper_window);
dnd_debug2 (" source window is %ld", dnd->dragger_window);
xdnd_convert_selection (dnd, dnd->dragger_window, dnd->dropper_window, dnd->desired_type);
dnd->stage = XDND_DROP_STAGE_CONVERTING;
} else {
dnd_debug1 (" will_accept is false - sending finished");
if (dnd_version_at_least (dnd->dragging_version, 2)) {
#if XDND_VERSION >= 3
xdnd_send_finished (dnd, dnd->dragger_window, dnd->dropper_toplevel, 1);
#else
xdnd_send_finished (dnd, dnd->dragger_window, xevent->xany.window, 1);
#endif
}
xdnd_xfree (dnd->dragger_typelist);
xdnd_reset (dnd);
dnd->stage = XDND_DROP_STAGE_IDLE;
}
result = 1;
} else {
dnd_debug1 (" wrong stage or from wrong window");
}
}
}
return result;
}
/*
Following here is a sample implementation: Suppose we want a window
@ -1550,50 +1373,241 @@ static int widget_apply_position (DndClass * dnd, Window widgets_window, Window
return 1;
}
Atom xdnd_get_drop (Display * display, XEvent * xevent, Atom * typelist, Atom * actionlist,
void handle_update(DndClass * dnd, XEvent * xevent){
dnd_debug2 (" message_type is XdndPosition to %ld", xevent->xany.window);
if (dnd->dragger_window == XDND_POSITION_SOURCE_WIN (xevent)) {
int want_position;
Atom action;
XRectangle rectangle;
Window last_window;
last_window = dnd->dropper_window;
#if XDND_VERSION >= 3
/* version 3 gives us the top-level window only. WE have to find the child that the pointer is over: */
if (1 || xevent->xany.window != dnd->dropper_toplevel || !dnd->dropper_window) {
Window parent, child, new_child = 0;
dnd->dropper_toplevel = xevent->xany.window;
parent = dnd->root_window;
child = dnd->dropper_toplevel;
for (;;) {
int xd, yd;
new_child = 0;
if (!XTranslateCoordinates (dnd->display, parent, child,
XDND_POSITION_ROOT_X (xevent), XDND_POSITION_ROOT_Y (xevent),
&xd, &yd, &new_child))
break;
if (!new_child)
break;
child = new_child;
}
dnd->dropper_window = xevent->xany.window = child;
dnd_debug2 (" child window translates to %ld", dnd->dropper_window);
} else if (xevent->xany.window == dnd->dropper_toplevel && dnd->dropper_window) {
xevent->xany.window = dnd->dropper_window;
dnd_debug2 (" child window previously found: %ld", dnd->dropper_window);
}
#endif
action = dnd->XdndActionCopy;
dnd->supported_action = dnd->XdndActionCopy;
dnd->x = XDND_POSITION_ROOT_X (xevent);
dnd->y = XDND_POSITION_ROOT_Y (xevent);
dnd->time = CurrentTime;
if (dnd_version_at_least (dnd->dragging_version, 1))
dnd->time = XDND_POSITION_TIME (xevent);
if (dnd_version_at_least (dnd->dragging_version, 1))
action = XDND_POSITION_ACTION (xevent);
#if XDND_VERSION >= 3
if (last_window && last_window != xevent->xany.window)
if (dnd->widget_apply_leave)
(*dnd->widget_apply_leave) (dnd, last_window);
#endif
dnd->will_accept = (*dnd->widget_apply_position) (dnd, xevent->xany.window, dnd->dragger_window,
action, dnd->x, dnd->y, dnd->time, dnd->dragger_typelist,
&want_position, &dnd->supported_action, &dnd->desired_type, &rectangle);
dnd_debug2 (" will accept = %d", dnd->will_accept);
#if XDND_VERSION >= 3
dnd_debug2 (" sending status of %ld", dnd->dropper_toplevel);
xdnd_send_status (dnd, dnd->dragger_window, dnd->dropper_toplevel, dnd->will_accept,
want_position, rectangle.x, rectangle.y, rectangle.width, rectangle.height, dnd->supported_action);
#else
dnd_debug2 (" sending status of %ld", xevent->xany.window);
xdnd_send_status (dnd, dnd->dragger_window, xevent->xany.window, dnd->will_accept,
want_position, rectangle.x, rectangle.y, rectangle.width, rectangle.height, dnd->supported_action);
#endif
} else {
dnd_debug1 (" wrong stage or from wrong window");
}
}
void handle_enter(DndClass * dnd, XEvent * xevent){
dnd_debug2 (" message_type is XdndEnter, version = %ld", XDND_ENTER_VERSION (xevent));
#if XDND_VERSION >= 3
if (XDND_ENTER_VERSION (xevent) < 3)
return;
#endif
xdnd_reset (dnd);
dnd->dragger_window = XDND_ENTER_SOURCE_WIN (xevent);
#if XDND_VERSION >= 3
dnd->dropper_toplevel = xevent->xany.window;
dnd->dropper_window = 0; /* enter goes to the top level window only,
so we don't really know what the
sub window is yet */
#else
dnd->dropper_window = xevent->xany.window;
#endif
xdnd_xfree (dnd->dragger_typelist);
if (XDND_ENTER_THREE_TYPES (xevent)) {
dnd_debug1 (" three types only");
xdnd_get_three_types (dnd, xevent, &dnd->dragger_typelist);
} else {
dnd_debug1 (" more than three types - getting list");
xdnd_get_type_list (dnd, dnd->dragger_window, &dnd->dragger_typelist);
}
dnd->dragging_version = XDND_ENTER_VERSION (xevent);
if (!dnd->dragger_typelist){
return;
}
for (;xevent->xclient.message_type != dnd->XdndPosition;)
{
XNextEvent(dnd->display, xevent);
}
handle_update(dnd, xevent);
/* Send request for obtain selection data. */
xdnd_convert_selection (dnd, dnd->dragger_window, dnd->dropper_window, dnd->desired_type);
/* Wait to receive selection. */
for (;xevent->type != SelectionNotify;)
{
XNextEvent(dnd->display, xevent);
}
if (xevent->xselection.property == dnd->Xdnd_NON_PROTOCOL_ATOM ) {
xdnd_get_selection (dnd, dnd->dragger_window, xevent->xselection.property, xevent->xany.window);
}
}
void handle_exit(DndClass * dnd, XEvent * xevent){
#if XDND_VERSION >= 3
if (xevent->xany.window == dnd->dropper_toplevel && dnd->dropper_window)
xevent->xany.window = dnd->dropper_window;
#endif
dnd_debug1 (" message_type is XdndLeave");
if (dnd->dragger_window == XDND_LEAVE_SOURCE_WIN (xevent) ) {
dnd_debug1 (" leaving");
if (dnd->widget_apply_leave)
(*dnd->widget_apply_leave) (dnd, xevent->xany.window);
xdnd_xfree (dnd->dragger_typelist);
dnd->dropper_toplevel = dnd->dropper_window = 0;
} else {
dnd_debug1 (" wrong stage or from wrong window");
}
}
void handle_drop(DndClass * dnd, XEvent * xevent){
#if XDND_VERSION >= 3
if (xevent->xany.window == dnd->dropper_toplevel && dnd->dropper_window)
xevent->xany.window = dnd->dropper_window;
#endif
dnd_debug1 (" message_type is XdndDrop");
if (dnd->dragger_window == XDND_DROP_SOURCE_WIN (xevent)) {
dnd->time = CurrentTime;
if (dnd_version_at_least (dnd->dragging_version, 1))
dnd->time = XDND_DROP_TIME (xevent);
if (dnd->will_accept) {
dnd_debug1 (" will_accept is true - converting selectiong");
dnd_debug2 (" my window is %ld", dnd->dropper_window);
dnd_debug2 (" source window is %ld", dnd->dragger_window);
xdnd_convert_selection (dnd, dnd->dragger_window, dnd->dropper_window, dnd->desired_type);
} else {
dnd_debug1 (" will_accept is false - sending finished");
if (dnd_version_at_least (dnd->dragging_version, 2)) {
#if XDND_VERSION >= 3
xdnd_send_finished (dnd, dnd->dragger_window, dnd->dropper_toplevel, 1);
#else
xdnd_send_finished (dnd, dnd->dragger_window, xevent->xany.window, 1);
#endif
}
xdnd_xfree (dnd->dragger_typelist);
xdnd_reset (dnd);
}
} else {
dnd_debug1 (" wrong stage or from wrong window");
}
/* Wait to receive selection. */
for (;xevent->type != SelectionNotify;)
{
XNextEvent(dnd->display, xevent);
}
if (xevent->xselection.property == dnd->Xdnd_NON_PROTOCOL_ATOM ) {
int error;
dnd_debug1 (" property is Xdnd_NON_PROTOCOL_ATOM - getting selection");
error = xdnd_get_selection (dnd, dnd->dragger_window, xevent->xselection.property, xevent->xany.window);
/* error is not actually used, i think future versions of the protocol maybe should return
an error status to the calling window with the XdndFinished client message */
if (dnd_version_at_least (dnd->dragging_version, 2)) {
#if XDND_VERSION >= 3
xdnd_send_finished (dnd, dnd->dragger_window, dnd->dropper_toplevel, error);
#else
xdnd_send_finished (dnd, dnd->dragger_window, dnd->dropper_window, error);
#endif
dnd_debug1 (" sending finished");
}
xdnd_xfree (dnd->dragger_typelist);
xdnd_reset (dnd);
}
}
GHOST_TEventType xdnd_handle_event (Display * display, XEvent * xevent, Atom * typelist, Atom * actionlist,
unsigned char **data, int *length, Atom * type, int *x, int *y)
{
Atom action = 0;
GHOST_TEventType event_type = 0;
static int initialised = 0;
static DndClass dnd;
if (!initialised) {
xdnd_init (&dnd, display);
if (!initialised)
{
xdnd_init(&dnd, display);
initialised = 1;
}
if (xevent->type != ClientMessage || xevent->xclient.message_type != dnd.XdndEnter) {
if (xevent->type != ClientMessage)
{
return 0;
} else {
struct xdnd_get_drop_info i;
/* setup user structure */
memset (&i, 0, sizeof (i));
}
static struct xdnd_get_drop_info i;
if (xevent->xclient.message_type == dnd.XdndEnter)
{
/* setup user structure */
memset(&i, 0, sizeof(i));
i.actionlist = actionlist;
i.typelist = typelist;
dnd.user_hook1 = &i;
/* setup methods */
/* setup methods */
dnd.widget_insert_drop = widget_insert_drop;
dnd.widget_apply_position = widget_apply_position;
/* main loop */
for (;;) {
xdnd_handle_drop_events (&dnd, xevent);
if (dnd.stage == XDND_DROP_STAGE_IDLE)
break;
XNextEvent (dnd.display, xevent);
}
/* return results */
if (i.drop_data) {
*length = i.drop_data_length;
*data = i.drop_data;
action = i.return_action;
*type = i.return_type;
*x = i.x;
*y = i.y;
}
event_type = GHOST_kEventDraggingEntered;
handle_enter(&dnd, xevent);
}
return action;
else if (xevent->xclient.message_type == dnd.XdndPosition)
{
event_type = GHOST_kEventDraggingUpdated;
handle_update(&dnd, xevent);
}
else if (xevent->xclient.message_type == dnd.XdndDrop)
{
event_type = GHOST_kEventDraggingDropDone;
handle_drop(&dnd, xevent);
}
else if (xevent->xclient.message_type == dnd.XdndLeave)
{
event_type = GHOST_kEventDraggingExited;
handle_exit(&dnd, xevent);
}
/* return results */
*length = i.drop_data_length;
i.drop_data_length=0;
*data = i.drop_data;
/* data wil be free outside */
i.drop_data=NULL;
*type = i.return_type;
*x = i.x;
*y = i.y;
return event_type;
}

4
extern/xdnd/xdnd.h vendored
View File

@ -16,7 +16,7 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307, USA.
*/
#include "../../intern/ghost/GHOST_Types.h"
#ifndef _X_DND_H
#define _X_DND_H
@ -208,7 +208,7 @@ Atom xdnd_drag (DndClass * dnd, Window from, Atom action, Atom * typelist);
libraries main event loop and be called if the event type is
ClientMessage or SelectionNotify */
int xdnd_handle_drop_events (DndClass * dnd, XEvent * xevent);
Atom xdnd_get_drop (Display * display, XEvent * xevent, Atom * typelist, Atom * actionlist,
GHOST_TEventType xdnd_handle_event (Display * display, XEvent * xevent, Atom * typelist, Atom * actionlist,
unsigned char **data, int *length, Atom * type, int *x, int *y);

View File

@ -81,12 +81,13 @@ HRESULT __stdcall GHOST_DropTargetWin32::DragEnter(IDataObject *p_data_object,
DWORD *pdw_effect)
{
/* We accept all drop by default. */
void *data = getGhostData(p_data_object);
m_window->setAcceptDragOperation(true);
*pdw_effect = DROPEFFECT_NONE;
m_draggedObjectType = getGhostType(p_data_object);
m_system->pushDragDropEvent(
GHOST_kEventDraggingEntered, m_draggedObjectType, m_window, pt.x, pt.y, NULL);
GHOST_kEventDraggingEntered, m_draggedObjectType, m_window, pt.x, pt.y, data);
return S_OK;
}

View File

@ -202,24 +202,25 @@ bool GHOST_DropTargetX11::GHOST_HandleClientMessage(XEvent *event)
uchar *dropBuffer;
int dropBufferSize, dropX, dropY;
if (xdnd_get_drop(m_system->getXDisplay(),
event,
m_dndTypes,
m_dndActions,
&dropBuffer,
&dropBufferSize,
&dropType,
&dropX,
&dropY)) {
void *data = getGhostData(dropType, dropBuffer, dropBufferSize);
GHOST_TEventType event_type = xdnd_handle_event(m_system->getXDisplay(),
event,
m_dndTypes,
m_dndActions,
&dropBuffer,
&dropBufferSize,
&dropType,
&dropX,
&dropY);
if (event_type > 0) {
if (data) {
m_system->pushDragDropEvent(
GHOST_kEventDraggingDropDone, m_draggedObjectType, m_window, dropX, dropY, data);
void *data = nullptr;
if (dropBufferSize > 0) {
data = getGhostData(dropType, dropBuffer, dropBufferSize);
}
m_system->pushDragDropEvent(event_type, m_draggedObjectType, m_window, dropX, dropY, data);
if (dropBufferSize > 0) {
free(dropBuffer);
}
free(dropBuffer);
m_draggedObjectType = GHOST_kDragnDropTypeUnknown;
return true;

View File

@ -70,7 +70,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(&path, 1);
but->dragflag |= UI_BUT_DRAGPOIN_FREE;
}

View File

@ -26,6 +26,7 @@ set(INC_SYS
)
set(SRC
io_utils.c
io_alembic.c
io_cache.c
io_collada.c
@ -38,6 +39,7 @@ set(SRC
io_stl_ops.c
io_usd.c
io_utils.h
io_alembic.h
io_cache.h
io_collada.h

View File

@ -38,6 +38,7 @@
# include "IO_path_util_types.h"
# include "IO_wavefront_obj.h"
# include "io_obj.h"
# include "io_utils.h"
static const EnumPropertyItem io_obj_export_evaluation_mode[] = {
{DAG_EVAL_RENDER, "DAG_EVAL_RENDER", 0, "Render", "Export objects as they appear in render"},
@ -370,12 +371,6 @@ void WM_OT_obj_export(struct wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_HIDDEN);
}
static int wm_obj_import_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
WM_event_add_fileselect(C, op);
return OPERATOR_RUNNING_MODAL;
}
static int wm_obj_import_exec(bContext *C, wmOperator *op)
{
struct OBJImportParams import_params;
@ -457,6 +452,7 @@ static void wm_obj_import_draw(bContext *C, wmOperator *op)
PointerRNA ptr;
wmWindowManager *wm = CTX_wm_manager(C);
RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr);
files_drop_info_draw(C, op, ICON_FILE_3D);
ui_obj_import_settings(op->layout, &ptr);
}
@ -467,9 +463,9 @@ void WM_OT_obj_import(struct wmOperatorType *ot)
ot->name = "Import Wavefront OBJ";
ot->description = "Load a Wavefront OBJ scene";
ot->idname = "WM_OT_obj_import";
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_PRESET;
ot->flag = OPTYPE_UNDO | OPTYPE_PRESET;
ot->invoke = wm_obj_import_invoke;
ot->invoke = wm_io_import_invoke;
ot->exec = wm_obj_import_exec;
ot->poll = WM_operator_winactive;
ot->ui = wm_obj_import_draw;
@ -482,6 +478,7 @@ void WM_OT_obj_import(struct wmOperatorType *ot)
WM_FILESEL_DIRECTORY | WM_FILESEL_FILES,
FILE_DEFAULTDISPLAY,
FILE_SORT_DEFAULT);
skip_save_import_paths_props(ot, WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILES);
RNA_def_float(
ot->srna,
"global_scale",
@ -533,4 +530,22 @@ void WM_OT_obj_import(struct wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_HIDDEN);
}
bool obj_file_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event))
{
if (drag->type == WM_DRAG_PATH) {
const eFileSel_File_Types file_type = WM_drag_get_path_file_type(drag);
if (file_type == FILE_TYPE_OBJECT_IO &&
BLI_path_extension_check(WM_drag_get_paths(drag)[0], ".obj")) {
return true;
}
}
return false;
}
void WM_obj_dropbox_add()
{
ListBase *lb = WM_dropboxmap_find("Window", 0, 0);
WM_dropbox_add(lb, "WM_OT_obj_import", obj_file_drop_poll, files_drop_copy, NULL, NULL);
}
#endif /* WITH_IO_WAVEFRONT_OBJ */

View File

@ -10,3 +10,4 @@ struct wmOperatorType;
void WM_OT_obj_export(struct wmOperatorType *ot);
void WM_OT_obj_import(struct wmOperatorType *ot);
void WM_obj_dropbox_add();

View File

@ -33,18 +33,22 @@ void ED_operatortypes_io(void)
/* Collada operators: */
WM_operatortype_append(WM_OT_collada_export);
WM_operatortype_append(WM_OT_collada_import);
// WM_collada_dropbox_add();
#endif
#ifdef WITH_ALEMBIC
WM_operatortype_append(WM_OT_alembic_import);
WM_operatortype_append(WM_OT_alembic_export);
// WM_alembic_dropbox_add();
#endif
#ifdef WITH_USD
WM_operatortype_append(WM_OT_usd_import);
WM_operatortype_append(WM_OT_usd_export);
// WM_usd_dropbox_add();
#endif
#ifdef WITH_IO_GPENCIL
WM_operatortype_append(WM_OT_gpencil_import_svg);
// WM_gpencil_dropbox_add();
# ifdef WITH_PUGIXML
WM_operatortype_append(WM_OT_gpencil_export_svg);
# endif
@ -63,14 +67,17 @@ void ED_operatortypes_io(void)
#ifdef WITH_IO_WAVEFRONT_OBJ
WM_operatortype_append(WM_OT_obj_export);
WM_operatortype_append(WM_OT_obj_import);
WM_obj_dropbox_add();
#endif
#ifdef WITH_IO_PLY
WM_operatortype_append(WM_OT_ply_export);
WM_operatortype_append(WM_OT_ply_import);
// WM_ply_dropbox_add();
#endif
#ifdef WITH_IO_STL
WM_operatortype_append(WM_OT_stl_import);
// WM_stl_dropbox_add();
#endif
}

View File

@ -0,0 +1,106 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#if defined(WITH_COLLADA) || defined(WITH_IO_GPENCIL) || defined(WITH_IO_WAVEFRONT_OBJ) || \
defined(WITH_IO_PLY) || defined(WITH_IO_STL) || defined(WITH_USD)
# include "DNA_space_types.h"
# include "BKE_context.h"
# include "BLI_path_util.h"
# include "BLI_string.h"
# include "BLI_utildefines.h"
# include "BLT_translation.h"
# include "ED_fileselect.h"
# include "RNA_access.h"
# include "RNA_define.h"
# include "UI_interface.h"
# include "WM_api.h"
# include "io_utils.h"
int wm_io_import_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
char filepath[FILE_MAX];
RNA_string_get(op->ptr, "filepath", filepath);
if (filepath[0]) {
return WM_operator_props_dialog_popup(C, op, 300);
}
WM_event_add_fileselect(C, op);
return OPERATOR_RUNNING_MODAL;
}
void skip_save_import_paths_props(wmOperatorType *ot, const eFileSel_Flag flag)
{
if (flag & WM_FILESEL_FILEPATH) {
RNA_def_property_flag(RNA_struct_type_find_property(ot->srna, "filepath"), PROP_SKIP_SAVE);
}
if (flag & WM_FILESEL_FILENAME) {
RNA_def_property_flag(RNA_struct_type_find_property(ot->srna, "filename"), PROP_SKIP_SAVE);
}
if (flag & WM_FILESEL_DIRECTORY) {
RNA_def_property_flag(RNA_struct_type_find_property(ot->srna, "directory"), PROP_SKIP_SAVE);
}
if (flag & WM_FILESEL_FILES) {
RNA_def_property_flag(RNA_struct_type_find_property(ot->srna, "files"), PROP_SKIP_SAVE);
}
if (flag & WM_FILESEL_RELPATH) {
RNA_def_property_flag(RNA_struct_type_find_property(ot->srna, "relative_path"),
PROP_SKIP_SAVE);
}
}
void files_drop_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBox *drop)
{
const char **paths = WM_drag_get_paths(drag);
RNA_string_set(drop->ptr, "filepath", paths[0]);
char dir[FILE_MAX], file[FILE_MAX];
BLI_split_dirfile(paths[0], dir, file, sizeof(dir), sizeof(file));
RNA_string_set(drop->ptr, "directory", dir);
RNA_collection_clear(drop->ptr, "files");
const int path_count = WM_drag_get_path_count(drag);
for (int i = 0; i < path_count; i++) {
BLI_split_dirfile(paths[i], dir, file, sizeof(dir), sizeof(file));
PointerRNA itemptr;
RNA_collection_add(drop->ptr, "files", &itemptr);
RNA_string_set(&itemptr, "name", file);
}
}
void file_drop_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBox *drop)
{
RNA_string_set(drop->ptr, "filepath", WM_drag_get_paths(drag)[0]);
}
void file_drop_info_draw(bContext *C, wmOperator *op, int icon)
{
ScrArea *area = CTX_wm_area(C);
if (area->spacetype != SPACE_FILE) {
char filepath[FILE_MAX];
RNA_string_get(op->ptr, "filepath", filepath);
uiLayout *box = uiLayoutBox(op->layout);
uiItemL(box, filepath, icon);
}
}
void files_drop_info_draw(bContext *C, wmOperator *op, int icon)
{
ScrArea *area = CTX_wm_area(C);
if (area->spacetype != SPACE_FILE) {
char label[FILE_MAX] = "Multiple files selected.";
if (RNA_collection_length(op->ptr, "files") == 1) {
RNA_string_get(op->ptr, "filepath", label);
}
uiLayout *box = uiLayoutBox(op->layout);
uiItemL(box, label, icon);
}
}
#endif

View File

@ -0,0 +1,27 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#if defined(WITH_COLLADA) || defined(WITH_IO_GPENCIL) || defined(WITH_IO_WAVEFRONT_OBJ) || \
defined(WITH_IO_PLY) || defined(WITH_IO_STL) || defined(WITH_USD)
# include "WM_types.h"
struct wmOperator;
struct wmOperatorType;
struct wmDrag;
struct wmDropBox;
int wm_io_import_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event));
void skip_save_import_paths_props(wmOperatorType *ot, const eFileSel_Flag flag);
void file_drop_copy(bContext *C, wmDrag *drag, wmDropBox *drop);
// Function that copies file directories for operators that support importing multiple files
void files_drop_copy(bContext *C, wmDrag *drag, wmDropBox *drop);
void file_drop_info_draw(bContext *C, wmOperator *op, int icon);
// Function that summarizes file information for operators that support importing multiple files
void files_drop_info_draw(bContext *C, wmOperator *op, int icon);
#endif

View File

@ -4564,7 +4564,7 @@ static void screen_animation_region_tag_redraw(
ED_region_tag_redraw(region);
}
//#define PROFILE_AUDIO_SYNCH
// #define PROFILE_AUDIO_SYNCH
static int screen_animation_step_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
{
@ -5756,7 +5756,7 @@ static bool blend_file_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEven
static void blend_file_drop_copy(bContext *UNUSED(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]);
}
void ED_keymap_screen(wmKeyConfig *keyconf)

View File

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

View File

@ -172,7 +172,7 @@ static bool path_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNU
static void path_drop_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBox *drop)
{
char pathname[FILE_MAX + 2];
BLI_snprintf(pathname, sizeof(pathname), "\"%s\"", WM_drag_get_path(drag));
BLI_snprintf(pathname, sizeof(pathname), "\"%s\"", WM_drag_get_paths(drag)[0]);
RNA_string_set(drop->ptr, "text", pathname);
}

View File

@ -783,7 +783,7 @@ static bool filepath_drop_poll(bContext *C, wmDrag *drag, const wmEvent *UNUSED(
static void filepath_drop_copy(bContext *UNUSED(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]);
}
/* 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 *UNUSED(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]);
}
/* area+region dropbox definition */

View File

@ -711,7 +711,7 @@ static void node_id_path_drop_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *dr
return;
}
const char *path = WM_drag_get_path(drag);
const char *path = WM_drag_get_paths(drag)[0];
if (path) {
RNA_string_set(drop->ptr, "filepath", path);
RNA_struct_property_unset(drop->ptr, "name");

View File

@ -247,7 +247,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_paths(drag)[0];
/* Path dropped. */
if (path) {
if (RNA_struct_find_property(drop->ptr, "filepath")) {
@ -335,7 +335,7 @@ static void get_drag_path(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], FILE_MAX);
}
}

View File

@ -312,7 +312,7 @@ static bool text_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNU
static void text_drop_copy(bContext *UNUSED(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]);
}
static bool text_drop_paste_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event))

View File

@ -903,7 +903,7 @@ static void view3d_id_path_drop_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *
RNA_struct_property_unset(drop->ptr, "filepath");
return;
}
const char *path = WM_drag_get_path(drag);
const char *path = WM_drag_get_paths(drag)[0];
if (path) {
RNA_string_set(drop->ptr, "filepath", path);
RNA_struct_property_unset(drop->ptr, "image");

View File

@ -1445,13 +1445,15 @@ const ListBase *WM_drag_asset_list_get(const wmDrag *drag);
const char *WM_drag_get_item_name(struct 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. Value 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.
* \param path_count: the number of paths in #paths
*/
wmDragPath *WM_drag_create_path_data(const char *path);
const char *WM_drag_get_path(const wmDrag *drag);
wmDragPath *WM_drag_create_path_data(const char **paths, int path_count);
const char **WM_drag_get_paths(const wmDrag *drag);
const int WM_drag_get_path_count(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

@ -1133,7 +1133,9 @@ typedef struct wmDragAssetListItem {
} wmDragAssetListItem;
typedef struct wmDragPath {
char *path;
char **paths;
char *tooltip;
int path_count;
/* 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 */

View File

@ -750,29 +750,70 @@ 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(const char **paths, int path_count)
{
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]);
int valid_path_count = 1;
for (int i = 1; i < path_count; i++) {
if ((ext == nullptr && BLI_path_extension(paths[i]) == nullptr) ||
(ext != nullptr && BLI_path_extension_check(paths[i], ext))) {
valid_path_count++;
}
}
path_data->paths = (char **)MEM_mallocN(valid_path_count * sizeof(char *), "paths");
int path_index = 0;
for (int i = 0; i < path_count; i++) {
if ((ext == nullptr && BLI_path_extension(paths[i]) == nullptr) ||
(ext != nullptr && BLI_path_extension_check(paths[i], ext))) {
path_data->paths[path_index] = BLI_strdup(paths[i]);
path_index++;
}
}
path_data->file_type = ED_path_extension_type(paths[0]);
path_data->path_count = valid_path_count;
if (path_count == 1) {
path_data->tooltip = BLI_strdup(paths[0]);
}
else {
const char *tooltip = TIP_("Dragging %d %s files.");
path_data->tooltip = BLI_sprintfN(tooltip, valid_path_count, ext);
}
return path_data;
}
static void wm_drag_free_path_data(wmDragPath **path_data)
{
MEM_freeN((*path_data)->path);
for (int i = 0; i < (*path_data)->path_count; i++) {
MEM_freeN((*path_data)->paths[i]);
}
MEM_freeN((*path_data)->paths);
MEM_freeN((*path_data)->tooltip);
MEM_delete(*path_data);
*path_data = nullptr;
}
const char *WM_drag_get_path(const wmDrag *drag)
const char **WM_drag_get_paths(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 const_cast<const char **>(path_data->paths);
}
const int WM_drag_get_path_count(const wmDrag *drag)
{
if (drag->type != WM_DRAG_PATH) {
return 0;
}
const wmDragPath *path_data = static_cast<const wmDragPath *>(drag->poin);
return path_data->path_count;
}
int WM_drag_get_path_file_type(const wmDrag *drag)
@ -836,7 +877,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;
}
case WM_DRAG_NAME:
return static_cast<const char *>(drag->poin);

View File

@ -1192,6 +1192,39 @@ void wm_window_reset_drawable(void)
}
}
void update_mouse_position_from_draganddrop(wmWindowManager *wm,
wmWindow *win,
wmEvent *event,
GHOST_TEventDragnDropData *ddd)
{
/* Ensure the event state matches modifiers (window was inactive). */
wm_window_update_eventstate_modifiers(wm, win);
/* Entering window, update mouse position (without sending an event). */
wm_window_update_eventstate(win);
wm_event_init_from_window(win, event); /* copy last state, like mouse coords */
/* activate region */
event->type = MOUSEMOVE;
event->val = KM_NOTHING;
copy_v2_v2_int(event->prev_xy, event->xy);
wm_cursor_position_from_ghost_screen_coords(win, &ddd->x, &ddd->y);
event->xy[0] = ddd->x;
event->xy[1] = ddd->y;
/* The values from #wm_window_update_eventstate may not match (under WAYLAND they don't)
* Write this into the event state. */
copy_v2_v2_int(win->eventstate->xy, event->xy);
event->flag = 0;
/* No context change! `C->wm->windrawable` is drawable, or for area queues. */
wm->winactive = win;
win->active = 1;
wm_event_add(win, event);
}
/**
* Called by ghost, here we handle events for windows themselves or send to event system.
*
@ -1439,37 +1472,40 @@ static bool ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_pt
}
break;
}
case GHOST_kEventDraggingEntered: {
WM_drag_free_list(&wm->drags);
wm_drags_exit(wm, win);
GHOST_TEventDragnDropData *ddd = GHOST_GetEventData(evt);
wmEvent event;
update_mouse_position_from_draganddrop(wm, win, &event, ddd);
/* add drag data to wm for paths: */
if (ddd->dataType == GHOST_kDragnDropTypeFilenames) {
GHOST_TStringArray *stra = ddd->data;
if (stra->count > 0) {
/* try to get icon type from extension */
int icon = ED_file_extension_icon((char *)stra->strings[0]);
wmDragPath *path_data = WM_drag_create_path_data((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 */
}
}
break;
}
case GHOST_kEventDraggingUpdated: {
GHOST_TEventDragnDropData *ddd = GHOST_GetEventData(evt);
wmEvent event;
update_mouse_position_from_draganddrop(wm, win, &event, ddd);
break;
}
case GHOST_kEventDraggingDropDone: {
GHOST_TEventDragnDropData *ddd = GHOST_GetEventData(evt);
/* Ensure the event state matches modifiers (window was inactive). */
wm_window_update_eventstate_modifiers(wm, win);
/* Entering window, update mouse position (without sending an event). */
wm_window_update_eventstate(win);
wmEvent event;
wm_event_init_from_window(win, &event); /* copy last state, like mouse coords */
/* activate region */
event.type = MOUSEMOVE;
event.val = KM_NOTHING;
copy_v2_v2_int(event.prev_xy, event.xy);
wm_cursor_position_from_ghost_screen_coords(win, &ddd->x, &ddd->y);
event.xy[0] = ddd->x;
event.xy[1] = ddd->y;
/* The values from #wm_window_update_eventstate may not match (under WAYLAND they don't)
* Write this into the event state. */
copy_v2_v2_int(win->eventstate->xy, event.xy);
event.flag = 0;
/* No context change! `C->wm->windrawable` is drawable, or for area queues. */
wm->winactive = win;
win->active = 1;
wm_event_add(win, &event);
update_mouse_position_from_draganddrop(wm, win, &event, ddd);
/* make blender drop event with custom data pointing to wm drags */
event.type = EVT_DROP;
@ -1479,27 +1515,29 @@ static bool ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_pt
event.customdata_free = true;
wm_event_add(win, &event);
// printf("Drop detected\n");
/* add drag data to wm for paths: */
if (ddd->dataType == GHOST_kDragnDropTypeFilenames) {
if (wm->drags.first != NULL && ddd->dataType == GHOST_kDragnDropTypeFilenames) {
GHOST_TStringArray *stra = ddd->data;
for (int a = 0; a < stra->count; a++) {
printf("drop file %s\n", stra->strings[a]);
if (stra->count > 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((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 */
}
}
break;
}
case GHOST_kEventDraggingExited: {
WM_drag_free_list(&wm->drags);
wm_drags_exit(wm, win);
GHOST_TEventDragnDropData *ddd = GHOST_GetEventData(evt);
wmEvent event;
update_mouse_position_from_draganddrop(wm, win, &event, ddd);
break;
}
case GHOST_kEventNativeResolutionChange: {
/* Only update if the actual pixel size changes. */
float prev_pixelsize = U.pixelsize;