UI Drag Drop: allow customizable drawing

No functional changes.

This commit adds 3 callbacks for `wmDropBox` which allow custom drawing
without affecting the internal dropbox API.

Differential Revision: https://developer.blender.org/D12948
This commit is contained in:
Germano Cavalcante 2021-10-25 10:07:00 -03:00
parent 2a7b9f2d45
commit e8027ec2a0
10 changed files with 205 additions and 152 deletions

@ -202,7 +202,7 @@ void ED_object_parent(struct Object *ob,
const char *substr); const char *substr);
char *ED_object_ot_drop_named_material_tooltip(struct bContext *C, char *ED_object_ot_drop_named_material_tooltip(struct bContext *C,
struct PointerRNA *properties, struct PointerRNA *properties,
const struct wmEvent *event); const int mval[2]);
/* bitflags for enter/exit editmode */ /* bitflags for enter/exit editmode */
enum { enum {

@ -40,12 +40,11 @@ static bool ui_tree_view_drop_poll(bContext *C, wmDrag *drag, const wmEvent *eve
static char *ui_tree_view_drop_tooltip(bContext *C, static char *ui_tree_view_drop_tooltip(bContext *C,
wmDrag *drag, wmDrag *drag,
const wmEvent *event, const int xy[2],
wmDropBox *UNUSED(drop)) wmDropBox *UNUSED(drop))
{ {
const ARegion *region = CTX_wm_region(C); const ARegion *region = CTX_wm_region(C);
const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(region, const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(region, xy);
event->xy);
if (!hovered_tree_item) { if (!hovered_tree_item) {
return nullptr; return nullptr;
} }

@ -2589,10 +2589,10 @@ void OBJECT_OT_make_single_user(wmOperatorType *ot)
char *ED_object_ot_drop_named_material_tooltip(bContext *C, char *ED_object_ot_drop_named_material_tooltip(bContext *C,
PointerRNA *properties, PointerRNA *properties,
const wmEvent *event) const int mval[2])
{ {
int mat_slot = 0; int mat_slot = 0;
Object *ob = ED_view3d_give_material_slot_under_cursor(C, event->mval, &mat_slot); Object *ob = ED_view3d_give_material_slot_under_cursor(C, mval, &mat_slot);
if (ob == NULL) { if (ob == NULL) {
return BLI_strdup(""); return BLI_strdup("");
} }

@ -868,7 +868,7 @@ static bool datastack_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
static char *datastack_drop_tooltip(bContext *UNUSED(C), static char *datastack_drop_tooltip(bContext *UNUSED(C),
wmDrag *drag, wmDrag *drag,
const wmEvent *UNUSED(event), const int UNUSED(xy[2]),
struct wmDropBox *UNUSED(drop)) struct wmDropBox *UNUSED(drop))
{ {
StackDropData *drop_data = drag->poin; StackDropData *drop_data = drag->poin;
@ -1201,11 +1201,13 @@ static bool collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event
static char *collection_drop_tooltip(bContext *C, static char *collection_drop_tooltip(bContext *C,
wmDrag *drag, wmDrag *drag,
const wmEvent *event, const int UNUSED(xy[2]),
wmDropBox *UNUSED(drop)) wmDropBox *UNUSED(drop))
{ {
wmWindowManager *wm = CTX_wm_manager(C);
const wmEvent *event = wm->winactive ? wm->winactive->eventstate : NULL;
CollectionDrop data; CollectionDrop data;
if (!event->shift && collection_drop_init(C, drag, event, &data)) { if (event && !event->shift && collection_drop_init(C, drag, event, &data)) {
TreeElement *te = data.te; TreeElement *te = data.te;
if (!data.from || event->ctrl) { if (!data.from || event->ctrl) {
return BLI_strdup(TIP_("Link inside Collection")); return BLI_strdup(TIP_("Link inside Collection"));

@ -532,12 +532,17 @@ static bool view3d_mat_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event
static char *view3d_mat_drop_tooltip(bContext *C, static char *view3d_mat_drop_tooltip(bContext *C,
wmDrag *drag, wmDrag *drag,
const wmEvent *event, const int xy[2],
struct wmDropBox *drop) struct wmDropBox *drop)
{ {
const char *name = WM_drag_get_item_name(drag); const char *name = WM_drag_get_item_name(drag);
ARegion *region = CTX_wm_region(C);
RNA_string_set(drop->ptr, "name", name); RNA_string_set(drop->ptr, "name", name);
return ED_object_ot_drop_named_material_tooltip(C, drop->ptr, event); int mval[2] = {
xy[0] - region->winrct.xmin,
xy[1] - region->winrct.ymin,
};
return ED_object_ot_drop_named_material_tooltip(C, drop->ptr, mval);
} }
static bool view3d_world_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) static bool view3d_world_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
@ -556,7 +561,7 @@ static bool view3d_object_data_drop_poll(bContext *C, wmDrag *drag, const wmEven
static char *view3d_object_data_drop_tooltip(bContext *UNUSED(C), static char *view3d_object_data_drop_tooltip(bContext *UNUSED(C),
wmDrag *UNUSED(drag), wmDrag *UNUSED(drag),
const wmEvent *UNUSED(event), const int UNUSED(xy[2]),
wmDropBox *UNUSED(drop)) wmDropBox *UNUSED(drop))
{ {
return BLI_strdup(TIP_("Create object instance from object-data")); return BLI_strdup(TIP_("Create object instance from object-data"));

@ -740,6 +740,10 @@ struct wmDropBox *WM_dropbox_add(
void (*copy)(struct wmDrag *, struct wmDropBox *), void (*copy)(struct wmDrag *, struct wmDropBox *),
void (*cancel)(struct Main *, struct wmDrag *, struct wmDropBox *), void (*cancel)(struct Main *, struct wmDrag *, struct wmDropBox *),
WMDropboxTooltipFunc tooltip); WMDropboxTooltipFunc tooltip);
void WM_drag_draw_default_fn(struct bContext *C,
struct wmWindow *win,
struct wmDrag *drag,
const int xy[2]);
ListBase *WM_dropboxmap_find(const char *idname, int spaceid, int regionid); ListBase *WM_dropboxmap_find(const char *idname, int spaceid, int regionid);
/* ID drag and drop */ /* ID drag and drop */

@ -1020,7 +1020,7 @@ typedef struct wmDragAssetListItem {
typedef char *(*WMDropboxTooltipFunc)(struct bContext *, typedef char *(*WMDropboxTooltipFunc)(struct bContext *,
struct wmDrag *, struct wmDrag *,
const struct wmEvent *event, const int xy[2],
struct wmDropBox *drop); struct wmDropBox *drop);
typedef struct wmDrag { typedef struct wmDrag {
@ -1038,8 +1038,11 @@ typedef struct wmDrag {
float scale; float scale;
int sx, sy; int sx, sy;
/** If filled, draws operator tooltip/operator name. */ /** Informs which dropbox is activated with the drag item.
char tooltip[200]; * When this value changes, the #draw_activate and #draw_deactivate dropbox callbacks are
* triggered.
*/
struct wmDropBox *active_dropbox;
unsigned int flags; unsigned int flags;
/** List of wmDragIDs, all are guaranteed to have the same ID type. */ /** List of wmDragIDs, all are guaranteed to have the same ID type. */
@ -1067,6 +1070,18 @@ typedef struct wmDropBox {
*/ */
void (*cancel)(struct Main *, struct wmDrag *, struct wmDropBox *); void (*cancel)(struct Main *, struct wmDrag *, struct wmDropBox *);
/** Override the default drawing function. */
void (*draw)(struct bContext *, struct wmWindow *, struct wmDrag *, const int *);
/** Called when pool returns true the first time. */
void (*draw_activate)(struct wmDropBox *, struct wmDrag *drag);
/** Called when pool returns false the first time or when the drag event ends. */
void (*draw_deactivate)(struct wmDropBox *, struct wmDrag *drag);
/** Custom data for drawing. */
void *draw_data;
/** Custom tooltip shown during dragging. */ /** Custom tooltip shown during dragging. */
WMDropboxTooltipFunc tooltip; WMDropboxTooltipFunc tooltip;

@ -43,6 +43,9 @@
#include "BKE_idtype.h" #include "BKE_idtype.h"
#include "BKE_lib_id.h" #include "BKE_lib_id.h"
#include "BKE_main.h" #include "BKE_main.h"
#include "BKE_screen.h"
#include "GHOST_C-api.h"
#include "BLO_readfile.h" #include "BLO_readfile.h"
@ -63,6 +66,7 @@
#include "WM_api.h" #include "WM_api.h"
#include "WM_types.h" #include "WM_types.h"
#include "wm_event_system.h" #include "wm_event_system.h"
#include "wm_window.h"
/* ****************************************************** */ /* ****************************************************** */
@ -229,6 +233,9 @@ void WM_drag_data_free(int dragtype, void *poin)
void WM_drag_free(wmDrag *drag) void WM_drag_free(wmDrag *drag)
{ {
if (drag->active_dropbox && drag->active_dropbox->draw_deactivate) {
drag->active_dropbox->draw_deactivate(drag->active_dropbox, drag);
}
if (drag->flags & WM_DRAG_FREE_DATA) { if (drag->flags & WM_DRAG_FREE_DATA) {
WM_drag_data_free(drag->type, drag->poin); WM_drag_data_free(drag->type, drag->poin);
} }
@ -250,11 +257,11 @@ void WM_drag_free_list(struct ListBase *lb)
} }
} }
static char *dropbox_tooltip(bContext *C, wmDrag *drag, const wmEvent *event, wmDropBox *drop) static char *dropbox_tooltip(bContext *C, wmDrag *drag, const int xy[2], wmDropBox *drop)
{ {
char *tooltip = NULL; char *tooltip = NULL;
if (drop->tooltip) { if (drop->tooltip) {
tooltip = drop->tooltip(C, drag, event, drop); tooltip = drop->tooltip(C, drag, xy, drop);
} }
if (!tooltip) { if (!tooltip) {
tooltip = BLI_strdup(WM_operatortype_name(drop->ot, drop->ptr)); tooltip = BLI_strdup(WM_operatortype_name(drop->ot, drop->ptr));
@ -286,7 +293,7 @@ static wmDropBox *dropbox_active(bContext *C,
} }
/* return active operator tooltip/name when mouse is in box */ /* return active operator tooltip/name when mouse is in box */
static char *wm_dropbox_active(bContext *C, wmDrag *drag, const wmEvent *event) static wmDropBox *wm_dropbox_active(bContext *C, wmDrag *drag, const wmEvent *event)
{ {
wmWindow *win = CTX_wm_window(C); wmWindow *win = CTX_wm_window(C);
wmDropBox *drop = dropbox_active(C, &win->handlers, drag, event); wmDropBox *drop = dropbox_active(C, &win->handlers, drag, event);
@ -298,10 +305,7 @@ static char *wm_dropbox_active(bContext *C, wmDrag *drag, const wmEvent *event)
ARegion *region = CTX_wm_region(C); ARegion *region = CTX_wm_region(C);
drop = dropbox_active(C, &region->handlers, drag, event); drop = dropbox_active(C, &region->handlers, drag, event);
} }
if (drop) { return drop;
return dropbox_tooltip(C, drag, event, drop);
}
return NULL;
} }
static void wm_drop_operator_options(bContext *C, wmDrag *drag, const wmEvent *event) static void wm_drop_operator_options(bContext *C, wmDrag *drag, const wmEvent *event)
@ -316,23 +320,17 @@ static void wm_drop_operator_options(bContext *C, wmDrag *drag, const wmEvent *e
return; return;
} }
drag->tooltip[0] = 0; wmDropBox *drop_prev = drag->active_dropbox;
wmDropBox *drop = wm_dropbox_active(C, drag, event);
/* check buttons (XXX todo rna and value) */ if (drop != drop_prev) {
if (UI_but_active_drop_name(C)) { if (drop_prev && drop_prev->draw_deactivate) {
BLI_strncpy(drag->tooltip, IFACE_("Paste name"), sizeof(drag->tooltip)); drop_prev->draw_deactivate(drop_prev, drag);
BLI_assert(drop_prev->draw_data == NULL);
} }
else { if (drop && drop->draw_activate) {
char *tooltip = wm_dropbox_active(C, drag, event); drop->draw_activate(drop, drag);
if (tooltip) {
BLI_strncpy(drag->tooltip, tooltip, sizeof(drag->tooltip));
MEM_freeN(tooltip);
// WM_cursor_modal_set(win, WM_CURSOR_COPY);
} }
// else drag->active_dropbox = drop;
// WM_cursor_modal_restore(win);
/* unsure about cursor type, feels to be too much */
} }
} }
@ -629,54 +627,16 @@ const char *WM_drag_get_item_name(wmDrag *drag)
return ""; return "";
} }
static void drag_rect_minmax(rcti *rect, int x1, int y1, int x2, int y2) static void wm_drag_draw_icon(bContext *UNUSED(C),
wmWindow *UNUSED(win),
wmDrag *drag,
const int xy[2])
{ {
if (rect->xmin > x1) {
rect->xmin = x1;
}
if (rect->xmax < x2) {
rect->xmax = x2;
}
if (rect->ymin > y1) {
rect->ymin = y1;
}
if (rect->ymax < y2) {
rect->ymax = y2;
}
}
/* called in wm_draw.c */
/* if rect set, do not draw */
void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect)
{
const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
wmWindowManager *wm = CTX_wm_manager(C);
const int winsize_y = WM_window_pixels_y(win);
int cursorx = win->eventstate->xy[0];
int cursory = win->eventstate->xy[1];
if (rect) {
rect->xmin = rect->xmax = cursorx;
rect->ymin = rect->ymax = cursory;
}
/* Should we support multi-line drag draws? Maybe not, more types mixed won't work well. */
GPU_blend(GPU_BLEND_ALPHA);
LISTBASE_FOREACH (wmDrag *, drag, &wm->drags) {
const uchar text_col[] = {255, 255, 255, 255};
int iconsize = UI_DPI_ICON_SIZE;
int padding = 4 * UI_DPI_FAC;
/* image or icon */
int x, y; int x, y;
if (drag->imb) { if (drag->imb) {
x = cursorx - drag->sx / 2; x = xy[0] - drag->sx / 2;
y = cursory - drag->sy / 2; y = xy[1] - drag->sy / 2;
if (rect) {
drag_rect_minmax(rect, x, y, x + drag->sx, y + drag->sy);
}
else {
float col[4] = {1.0f, 1.0f, 1.0f, 0.65f}; /* this blends texture */ float col[4] = {1.0f, 1.0f, 1.0f, 0.65f}; /* this blends texture */
IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR); IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR);
immDrawPixelsTexScaled(&state, immDrawPixelsTexScaled(&state,
@ -693,68 +653,136 @@ void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect)
1.0f, 1.0f,
col); col);
} }
}
else { else {
x = cursorx - 2 * padding; int padding = 4 * UI_DPI_FAC;
y = cursory - 2 * UI_DPI_FAC; x = xy[0] - 2 * padding;
y = xy[1] - 2 * UI_DPI_FAC;
if (rect) { const uchar text_col[] = {255, 255, 255, 255};
drag_rect_minmax(rect, x, y, x + iconsize, y + iconsize);
}
else {
UI_icon_draw_ex(x, y, drag->icon, U.inv_dpi_fac, 0.8, 0.0f, text_col, false); UI_icon_draw_ex(x, y, drag->icon, U.inv_dpi_fac, 0.8, 0.0f, text_col, false);
} }
} }
/* item name */ static void wm_drag_draw_item_name(wmDrag *drag, const int x, const int y)
if (drag->imb) { {
x = cursorx - drag->sx / 2; const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
y = cursory - drag->sy / 2 - iconsize; const uchar text_col[] = {255, 255, 255, 255};
}
else {
x = cursorx + 10 * UI_DPI_FAC;
y = cursory + 1 * UI_DPI_FAC;
}
if (rect) {
int w = UI_fontstyle_string_width(fstyle, WM_drag_get_item_name(drag));
drag_rect_minmax(rect, x, y, x + w, y + iconsize);
}
else {
UI_fontstyle_draw_simple(fstyle, x, y, WM_drag_get_item_name(drag), text_col); UI_fontstyle_draw_simple(fstyle, x, y, WM_drag_get_item_name(drag), text_col);
} }
/* operator name with roundbox */ static void wm_drag_draw_tooltip(bContext *C, wmWindow *win, wmDrag *drag, const int xy[2])
if (drag->tooltip[0]) { {
if (!CTX_wm_region(C)) {
/* Some callbacks require the region. */
return;
}
int iconsize = UI_DPI_ICON_SIZE;
int padding = 4 * UI_DPI_FAC;
char *tooltip = NULL;
bool free_tooltip = false;
if (UI_but_active_drop_name(C)) {
tooltip = IFACE_("Paste name");
}
else if (drag->active_dropbox) {
tooltip = dropbox_tooltip(C, drag, xy, drag->active_dropbox);
free_tooltip = true;
}
if (tooltip) {
const int winsize_y = WM_window_pixels_y(win);
int x, y;
if (drag->imb) { if (drag->imb) {
x = cursorx - drag->sx / 2; x = xy[0] - drag->sx / 2;
if (cursory + drag->sy / 2 + padding + iconsize < winsize_y) { if (xy[1] + drag->sy / 2 + padding + iconsize < winsize_y) {
y = cursory + drag->sy / 2 + padding; y = xy[1] + drag->sy / 2 + padding;
} }
else { else {
y = cursory - drag->sy / 2 - padding - iconsize - padding - iconsize; y = xy[1] - drag->sy / 2 - padding - iconsize - padding - iconsize;
} }
} }
else { else {
x = cursorx - 2 * padding; x = xy[0] - 2 * padding;
if (cursory + iconsize + iconsize < winsize_y) { if (xy[1] + iconsize + iconsize < winsize_y) {
y = (cursory + iconsize) + padding; y = (xy[1] + iconsize) + padding;
} }
else { else {
y = (cursory - iconsize) - padding; y = (xy[1] - iconsize) - padding;
} }
} }
if (rect) { wm_drop_operator_draw(tooltip, x, y);
int w = UI_fontstyle_string_width(fstyle, WM_drag_get_item_name(drag)); if (free_tooltip) {
drag_rect_minmax(rect, x, y, x + w, y + iconsize); MEM_freeN(tooltip);
}
}
}
static void wm_drag_draw_default(bContext *C, wmWindow *win, wmDrag *drag, const int xy[2])
{
int xy_tmp[2] = {UNPACK2(xy)};
/* Image or icon. */
wm_drag_draw_icon(C, win, drag, xy_tmp);
/* Item name. */
if (drag->imb) {
int iconsize = UI_DPI_ICON_SIZE;
xy_tmp[0] = xy[0] - (drag->sx / 2);
xy_tmp[1] = xy[1] - (drag->sy / 2) - iconsize;
} }
else { else {
wm_drop_operator_draw(drag->tooltip, x, y); xy_tmp[0] = xy[0] + 10 * UI_DPI_FAC;
xy_tmp[1] = xy[1] + 1 * UI_DPI_FAC;
} }
wm_drag_draw_item_name(drag, UNPACK2(xy_tmp));
/* Operator name with roundbox. */
wm_drag_draw_tooltip(C, win, drag, xy);
} }
void WM_drag_draw_default_fn(bContext *C, wmWindow *win, wmDrag *drag, const int xy[2])
{
wm_drag_draw_default(C, win, drag, xy);
}
/* Called in #wm_draw_window_onscreen. */
void wm_drags_draw(bContext *C, wmWindow *win)
{
int xy[2];
if (ELEM(win->grabcursor, GHOST_kGrabWrap, GHOST_kGrabHide)) {
wm_cursor_position_get(win, &xy[0], &xy[1]);
}
else {
xy[0] = win->eventstate->xy[0];
xy[1] = win->eventstate->xy[1];
}
/* Set a region. It is used in the `UI_but_active_drop_name`. */
bScreen *screen = CTX_wm_screen(C);
ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, UNPACK2(xy));
ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_ANY, UNPACK2(xy));
if (region) {
BLI_assert(!CTX_wm_area(C) && !CTX_wm_region(C));
CTX_wm_area_set(C, area);
CTX_wm_region_set(C, region);
}
wmWindowManager *wm = CTX_wm_manager(C);
/* Should we support multi-line drag draws? Maybe not, more types mixed won't work well. */
GPU_blend(GPU_BLEND_ALPHA);
LISTBASE_FOREACH (wmDrag *, drag, &wm->drags) {
if (drag->active_dropbox && drag->active_dropbox->draw) {
drag->active_dropbox->draw(C, win, drag, xy);
continue;
}
wm_drag_draw_default(C, win, drag, xy);
} }
GPU_blend(GPU_BLEND_NONE); GPU_blend(GPU_BLEND_NONE);
CTX_wm_area_set(C, NULL);
CTX_wm_region_set(C, NULL);
} }

@ -856,9 +856,9 @@ static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view)
wm_gesture_draw(win); wm_gesture_draw(win);
} }
/* needs pixel coords in screen */ /* Needs pixel coords in screen. */
if (wm->drags.first) { if (wm->drags.first) {
wm_drags_draw(C, win, NULL); wm_drags_draw(C, win);
} }
GPU_debug_group_end(); GPU_debug_group_end();

@ -173,7 +173,7 @@ void wm_tablet_data_from_ghost(const struct GHOST_TabletData *tablet_data, wmTab
/* wm_dropbox.c */ /* wm_dropbox.c */
void wm_dropbox_free(void); void wm_dropbox_free(void);
void wm_drags_check_ops(bContext *C, const wmEvent *event); void wm_drags_check_ops(bContext *C, const wmEvent *event);
void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect); void wm_drags_draw(bContext *C, wmWindow *win);
#ifdef __cplusplus #ifdef __cplusplus
} }