diff options
Diffstat (limited to 'source/blender/windowmanager/intern/wm_dragdrop.c')
-rw-r--r-- | source/blender/windowmanager/intern/wm_dragdrop.c | 449 |
1 files changed, 308 insertions, 141 deletions
diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index 9af90355a79..b9f0e09d106 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -40,9 +40,13 @@ #include "BKE_context.h" #include "BKE_global.h" +#include "BKE_idprop.h" #include "BKE_idtype.h" #include "BKE_lib_id.h" #include "BKE_main.h" +#include "BKE_screen.h" + +#include "GHOST_C-api.h" #include "BLO_readfile.h" @@ -63,6 +67,7 @@ #include "WM_api.h" #include "WM_types.h" #include "wm_event_system.h" +#include "wm_window.h" /* ****************************************************** */ @@ -176,6 +181,7 @@ wmDrag *WM_event_start_drag( } break; case WM_DRAG_ASSET: + case WM_DRAG_ASSET_CATALOG: /* Move ownership of poin to wmDrag. */ drag->poin = poin; drag->flags |= WM_DRAG_FREE_DATA; @@ -202,6 +208,31 @@ wmDrag *WM_event_start_drag( return drag; } +/** + * Additional work to cleanly end dragging. Additional because this doesn't actually remove the + * drag items. Should be called whenever dragging is stopped (successful or not, also when + * canceled). + */ +void wm_drags_exit(wmWindowManager *wm, wmWindow *win) +{ + bool any_active = false; + LISTBASE_FOREACH (const wmDrag *, drag, &wm->drags) { + if (drag->active_dropbox) { + any_active = true; + break; + } + } + + /* If there is no active drop-box #wm_drags_check_ops() set a stop-cursor, which needs to be + * restored. */ + if (!any_active) { + WM_cursor_modal_restore(win); + /* Ensure the correct area cursor is restored. */ + win->tag_cursor_refresh = true; + WM_event_add_mousemove(win); + } +} + void WM_event_drag_image(wmDrag *drag, ImBuf *imb, float scale, int sx, int sy) { drag->imb = imb; @@ -229,9 +260,15 @@ void WM_drag_data_free(int dragtype, void *poin) 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) { WM_drag_data_free(drag->type, drag->poin); } + if (drag->free_disabled_info) { + MEM_SAFE_FREE(drag->disabled_info); + } BLI_freelistN(&drag->ids); LISTBASE_FOREACH_MUTABLE (wmDragAssetListItem *, asset_item, &drag->asset_items) { if (asset_item->is_external) { @@ -250,11 +287,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; if (drop->tooltip) { - tooltip = drop->tooltip(C, drag, event, drop); + tooltip = drop->tooltip(C, drag, xy, drop); } if (!tooltip) { tooltip = BLI_strdup(WM_operatortype_name(drop->ot, drop->ptr)); @@ -269,15 +306,35 @@ static wmDropBox *dropbox_active(bContext *C, wmDrag *drag, const wmEvent *event) { + if (drag->free_disabled_info) { + MEM_SAFE_FREE(drag->disabled_info); + } + drag->disabled_info = NULL; + LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) { if (handler_base->type == WM_HANDLER_TYPE_DROPBOX) { wmEventHandler_Dropbox *handler = (wmEventHandler_Dropbox *)handler_base; if (handler->dropboxes) { LISTBASE_FOREACH (wmDropBox *, drop, handler->dropboxes) { - if (drop->poll(C, drag, event) && - WM_operator_poll_context(C, drop->ot, drop->opcontext)) { + if (!drop->poll(C, drag, event)) { + /* If the drop's poll fails, don't set the disabled-info. This would be too aggressive. + * Instead show it only if the drop box could be used in principle, but the operator + * can't be executed. */ + continue; + } + + if (WM_operator_poll_context(C, drop->ot, drop->opcontext)) { return drop; } + + /* Attempt to set the disabled hint when the poll fails. Will always be the last hint set + * when there are multiple failing polls (could allow multiple disabled-hints too). */ + bool free_disabled_info = false; + const char *disabled_hint = CTX_wm_operator_poll_msg_get(C, &free_disabled_info); + if (disabled_hint) { + drag->disabled_info = disabled_hint; + drag->free_disabled_info = free_disabled_info; + } } } } @@ -286,7 +343,7 @@ static wmDropBox *dropbox_active(bContext *C, } /* 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); wmDropBox *drop = dropbox_active(C, &win->handlers, drag, event); @@ -298,13 +355,13 @@ static char *wm_dropbox_active(bContext *C, wmDrag *drag, const wmEvent *event) ARegion *region = CTX_wm_region(C); drop = dropbox_active(C, ®ion->handlers, drag, event); } - if (drop) { - return dropbox_tooltip(C, drag, event, drop); - } - return NULL; + return drop; } -static void wm_drop_operator_options(bContext *C, wmDrag *drag, const wmEvent *event) +/** + * Update dropping information for the current mouse position in \a event. + */ +static void wm_drop_update_active(bContext *C, wmDrag *drag, const wmEvent *event) { wmWindow *win = CTX_wm_window(C); const int winsize_x = WM_window_pixels_x(win); @@ -316,24 +373,30 @@ static void wm_drop_operator_options(bContext *C, wmDrag *drag, const wmEvent *e return; } - drag->tooltip[0] = 0; - - /* check buttons (XXX todo rna and value) */ - if (UI_but_active_drop_name(C)) { - BLI_strncpy(drag->tooltip, IFACE_("Paste name"), sizeof(drag->tooltip)); + wmDropBox *drop_prev = drag->active_dropbox; + wmDropBox *drop = wm_dropbox_active(C, drag, event); + if (drop != drop_prev) { + if (drop_prev && drop_prev->draw_deactivate) { + drop_prev->draw_deactivate(drop_prev, drag); + BLI_assert(drop_prev->draw_data == NULL); + } + if (drop && drop->draw_activate) { + drop->draw_activate(drop, drag); + } + drag->active_dropbox = drop; } - else { - char *tooltip = wm_dropbox_active(C, drag, event); +} - if (tooltip) { - BLI_strncpy(drag->tooltip, tooltip, sizeof(drag->tooltip)); - MEM_freeN(tooltip); - // WM_cursor_modal_set(win, WM_CURSOR_COPY); - } - // else - // WM_cursor_modal_restore(win); - /* unsure about cursor type, feels to be too much */ +void wm_drop_prepare(bContext *C, wmDrag *drag, wmDropBox *drop) +{ + /* Optionally copy drag information to operator properties. Don't call it if the + * operator fails anyway, it might do more than just set properties (e.g. + * typically import an asset). */ + if (drop->copy && WM_operator_poll_context(C, drop->ot, drop->opcontext)) { + drop->copy(drag, drop); } + + wm_drags_exit(CTX_wm_manager(C), CTX_wm_window(C)); } /* called in inner handler loop, region context */ @@ -341,8 +404,19 @@ void wm_drags_check_ops(bContext *C, const wmEvent *event) { wmWindowManager *wm = CTX_wm_manager(C); + bool any_active = false; LISTBASE_FOREACH (wmDrag *, drag, &wm->drags) { - wm_drop_operator_options(C, drag, event); + wm_drop_update_active(C, drag, event); + + if (drag->active_dropbox) { + any_active = true; + } + } + + /* Change the cursor to display that dropping isn't possible here. But only if there is something + * being dragged actually. Cursor will be restored in #wm_drags_exit(). */ + if (!BLI_listbase_is_empty(&wm->drags)) { + WM_cursor_modal_set(CTX_wm_window(C), any_active ? WM_CURSOR_DEFAULT : WM_CURSOR_STOP); } } @@ -407,11 +481,15 @@ bool WM_drag_is_ID_type(const wmDrag *drag, int idcode) /** * \note: Does not store \a asset in any way, so it's fine to pass a temporary. */ -wmDragAsset *WM_drag_create_asset_data(const AssetHandle *asset, const char *path, int import_type) +wmDragAsset *WM_drag_create_asset_data(const AssetHandle *asset, + AssetMetaData *metadata, + const char *path, + int import_type) { wmDragAsset *asset_drag = MEM_mallocN(sizeof(*asset_drag), "wmDragAsset"); BLI_strncpy(asset_drag->name, ED_asset_handle_get_name(asset), sizeof(asset_drag->name)); + asset_drag->metadata = metadata; asset_drag->path = path; asset_drag->id_type = ED_asset_handle_get_id_type(asset); asset_drag->import_type = import_type; @@ -435,8 +513,30 @@ wmDragAsset *WM_drag_get_asset_data(const wmDrag *drag, int idcode) return (ELEM(idcode, 0, asset_drag->id_type)) ? asset_drag : NULL; } -static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag) +struct AssetMetaData *WM_drag_get_asset_meta_data(const wmDrag *drag, int idcode) +{ + wmDragAsset *drag_asset = WM_drag_get_asset_data(drag, idcode); + if (drag_asset) { + return drag_asset->metadata; + } + + ID *local_id = WM_drag_get_local_ID(drag, idcode); + if (local_id) { + return local_id->asset_data; + } + + return NULL; +} + +/** + * \param flag_extra: Additional linking flags (from #eFileSel_Params_Flag). + */ +ID *WM_drag_asset_id_import(wmDragAsset *asset_drag, const int flag_extra) { + /* Only support passing in limited flags. */ + BLI_assert(flag_extra == (flag_extra & FILE_AUTOSELECT)); + eFileSel_Params_Flag flag = flag_extra | FILE_ACTIVE_COLLECTION; + const char *name = asset_drag->name; ID_Type idtype = asset_drag->id_type; @@ -450,14 +550,8 @@ static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag) switch ((eFileAssetImportType)asset_drag->import_type) { case FILE_ASSET_IMPORT_LINK: - return WM_file_link_datablock(bmain, - scene, - view_layer, - view3d, - asset_drag->path, - idtype, - name, - FILE_ACTIVE_COLLECTION); + return WM_file_link_datablock( + bmain, scene, view_layer, view3d, asset_drag->path, idtype, name, flag); case FILE_ASSET_IMPORT_APPEND: return WM_file_append_datablock(bmain, scene, @@ -466,7 +560,7 @@ static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag) asset_drag->path, idtype, name, - BLO_LIBLINK_APPEND_RECURSIVE | FILE_ACTIVE_COLLECTION | + flag | BLO_LIBLINK_APPEND_RECURSIVE | BLO_LIBLINK_APPEND_ASSET_DATA_CLEAR); case FILE_ASSET_IMPORT_APPEND_REUSE: return WM_file_append_datablock(G_MAIN, @@ -476,7 +570,7 @@ static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag) asset_drag->path, idtype, name, - BLO_LIBLINK_APPEND_RECURSIVE | FILE_ACTIVE_COLLECTION | + flag | BLO_LIBLINK_APPEND_RECURSIVE | BLO_LIBLINK_APPEND_ASSET_DATA_CLEAR | BLO_LIBLINK_APPEND_LOCAL_ID_REUSE); } @@ -485,12 +579,24 @@ static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag) return NULL; } +bool WM_drag_asset_will_import_linked(const wmDrag *drag) +{ + if (drag->type != WM_DRAG_ASSET) { + return false; + } + + const wmDragAsset *asset_drag = WM_drag_get_asset_data(drag, 0); + return asset_drag->import_type == FILE_ASSET_IMPORT_LINK; +} + /** * When dragging a local ID, return that. Otherwise, if dragging an asset-handle, link or append * that depending on what was chosen by the drag-box (currently append only in fact). * * Use #WM_drag_free_imported_drag_ID() as cancel callback of the drop-box, so that the asset * import is rolled back if the drop operator fails. + * + * \param flag: #eFileSel_Params_Flag passed to linking code. */ ID *WM_drag_get_local_ID_or_import_from_asset(const wmDrag *drag, int idcode) { @@ -508,7 +614,7 @@ ID *WM_drag_get_local_ID_or_import_from_asset(const wmDrag *drag, int idcode) } /* Link/append the asset. */ - return wm_drag_asset_id_import(asset_drag); + return WM_drag_asset_id_import(asset_drag, 0); } /** @@ -544,6 +650,15 @@ void WM_drag_free_imported_drag_ID(struct Main *bmain, wmDrag *drag, wmDropBox * } } +wmDragAssetCatalog *WM_drag_get_asset_catalog_data(const wmDrag *drag) +{ + if (drag->type != WM_DRAG_ASSET_CATALOG) { + return NULL; + } + + return drag->poin; +} + /** * \note: Does not store \a asset in any way, so it's fine to pass a temporary. */ @@ -568,11 +683,12 @@ void WM_drag_add_asset_list_item( drag_asset->asset_data.local_id = local_id; } else { + AssetMetaData *metadata = ED_asset_handle_get_metadata(asset); char asset_blend_path[FILE_MAX_LIBEXTRA]; ED_asset_handle_get_full_library_path(C, asset_library_ref, asset, asset_blend_path); drag_asset->is_external = true; drag_asset->asset_data.external_info = WM_drag_create_asset_data( - asset, BLI_strdup(asset_blend_path), FILE_ASSET_IMPORT_APPEND); + asset, metadata, BLI_strdup(asset_blend_path), FILE_ASSET_IMPORT_APPEND); } BLI_addtail(&drag->asset_items, drag_asset); } @@ -603,6 +719,17 @@ static void wm_drop_operator_draw(const char *name, int x, int y) UI_fontstyle_draw_simple_backdrop(fstyle, x, y, name, col_fg, col_bg); } +static void wm_drop_redalert_draw(const char *redalert_str, int x, int y) +{ + const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; + const float col_bg[4] = {0.0f, 0.0f, 0.0f, 0.2f}; + float col_fg[4]; + + UI_GetThemeColor4fv(TH_REDALERT, col_fg); + + UI_fontstyle_draw_simple_backdrop(fstyle, x, y, redalert_str, col_fg, col_bg); +} + const char *WM_drag_get_item_name(wmDrag *drag) { switch (drag->type) { @@ -629,132 +756,172 @@ const char *WM_drag_get_item_name(wmDrag *drag) 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; + int x, y; + if (drag->imb) { + x = xy[0] - drag->sx / 2; + y = xy[1] - drag->sy / 2; + + float col[4] = {1.0f, 1.0f, 1.0f, 0.65f}; /* this blends texture */ + IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR); + immDrawPixelsTexScaled(&state, + x, + y, + drag->imb->x, + drag->imb->y, + GPU_RGBA8, + false, + drag->imb->rect, + drag->scale, + drag->scale, + 1.0f, + 1.0f, + col); } - if (rect->ymin > y1) { - rect->ymin = y1; - } - if (rect->ymax < y2) { - rect->ymax = y2; + else { + int padding = 4 * UI_DPI_FAC; + x = xy[0] - 2 * padding; + y = xy[1] - 2 * UI_DPI_FAC; + + const uchar text_col[] = {255, 255, 255, 255}; + UI_icon_draw_ex(x, y, drag->icon, U.inv_dpi_fac, 0.8, 0.0f, text_col, false); } } -/* called in wm_draw.c */ -/* if rect set, do not draw */ -void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect) +static void wm_drag_draw_item_name(wmDrag *drag, const int x, const int y) { const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; - wmWindowManager *wm = CTX_wm_manager(C); - const int winsize_y = WM_window_pixels_y(win); + const uchar text_col[] = {255, 255, 255, 255}; + UI_fontstyle_draw_simple(fstyle, x, y, WM_drag_get_item_name(drag), text_col); +} - int cursorx = win->eventstate->xy[0]; - int cursory = win->eventstate->xy[1]; - if (rect) { - rect->xmin = rect->xmax = cursorx; - rect->ymin = rect->ymax = cursory; - } +void WM_drag_draw_item_name_fn(bContext *UNUSED(C), + wmWindow *UNUSED(win), + wmDrag *drag, + const int xy[2]) +{ + int x = xy[0] + 10 * UI_DPI_FAC; + int y = xy[1] + 1 * UI_DPI_FAC; - /* 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; + wm_drag_draw_item_name(drag, x, y); +} - /* image or icon */ - int x, y; - if (drag->imb) { - x = cursorx - drag->sx / 2; - y = cursory - drag->sy / 2; +static void wm_drag_draw_tooltip(bContext *C, wmWindow *win, wmDrag *drag, const int xy[2]) +{ + if (!CTX_wm_region(C)) { + /* Some callbacks require the region. */ + return; + } + int iconsize = UI_DPI_ICON_SIZE; + int padding = 4 * UI_DPI_FAC; - 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 */ - IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR); - immDrawPixelsTexScaled(&state, - x, - y, - drag->imb->x, - drag->imb->y, - GPU_RGBA8, - false, - drag->imb->rect, - drag->scale, - drag->scale, - 1.0f, - 1.0f, - col); - } - } - else { - x = cursorx - 2 * padding; - y = cursory - 2 * UI_DPI_FAC; + char *tooltip = NULL; + if (drag->active_dropbox) { + tooltip = dropbox_tooltip(C, drag, xy, drag->active_dropbox); + } - if (rect) { - 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); - } - } + if (!tooltip && !drag->disabled_info) { + return; + } + + const int winsize_y = WM_window_pixels_y(win); + int x, y; + if (drag->imb) { + x = xy[0] - drag->sx / 2; - /* item name */ - if (drag->imb) { - x = cursorx - drag->sx / 2; - y = cursory - drag->sy / 2 - iconsize; + if (xy[1] + drag->sy / 2 + padding + iconsize < winsize_y) { + y = xy[1] + drag->sy / 2 + padding; } else { - x = cursorx + 10 * UI_DPI_FAC; - y = cursory + 1 * UI_DPI_FAC; + y = xy[1] - drag->sy / 2 - padding - iconsize - padding - iconsize; } + } + else { + x = xy[0] - 2 * padding; - 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); + if (xy[1] + iconsize + iconsize < winsize_y) { + y = (xy[1] + iconsize) + padding; } else { - UI_fontstyle_draw_simple(fstyle, x, y, WM_drag_get_item_name(drag), text_col); + y = (xy[1] - iconsize) - padding; } + } - /* operator name with roundbox */ - if (drag->tooltip[0]) { - if (drag->imb) { - x = cursorx - drag->sx / 2; + if (tooltip) { + wm_drop_operator_draw(tooltip, x, y); + MEM_freeN(tooltip); + } + else if (drag->disabled_info) { + wm_drop_redalert_draw(drag->disabled_info, x, y); + } +} - if (cursory + drag->sy / 2 + padding + iconsize < winsize_y) { - y = cursory + drag->sy / 2 + padding; - } - else { - y = cursory - drag->sy / 2 - padding - iconsize - padding - iconsize; - } - } - else { - x = cursorx - 2 * padding; +static void wm_drag_draw_default(bContext *C, wmWindow *win, wmDrag *drag, const int xy[2]) +{ + int xy_tmp[2] = {UNPACK2(xy)}; - if (cursory + iconsize + iconsize < winsize_y) { - y = (cursory + iconsize) + padding; - } - else { - y = (cursory - iconsize) - padding; - } - } + /* Image or icon. */ + wm_drag_draw_icon(C, win, drag, xy_tmp); - 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 { - wm_drop_operator_draw(drag->tooltip, x, y); - } + /* 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 { + 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]; + } + + 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); + CTX_wm_area_set(C, NULL); + CTX_wm_region_set(C, NULL); } |