Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian Eisel <julian@blender.org>2021-10-26 19:04:45 +0300
committerJulian Eisel <julian@blender.org>2021-10-26 19:14:17 +0300
commitdf2e05393533bff6b14751deb890ef0fc4064c8f (patch)
tree09eaaf20d2e5e8fed54b7cf6966db1d2cd88b69a /source/blender
parentec831ce5df86cbdbed8030d8a056f8a9b4eb0273 (diff)
UI: Improved feedback when dropping is not possible on drag 'n drop
* Allow operators to show a "disabled hint" in red text explaining why dropping at the current location and in current context doesn't work. Should greatly help users to understand what's the problem. * Show a "stop" cursor when dropping isn't possible, like it's common on OSes. Differential Revision: https://developer.blender.org/D10358
Diffstat (limited to 'source/blender')
-rw-r--r--source/blender/windowmanager/WM_types.h5
-rw-r--r--source/blender/windowmanager/intern/wm_dragdrop.c135
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c10
-rw-r--r--source/blender/windowmanager/wm_event_system.h2
4 files changed, 124 insertions, 28 deletions
diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h
index 2813047f0e4..1f1b1a70685 100644
--- a/source/blender/windowmanager/WM_types.h
+++ b/source/blender/windowmanager/WM_types.h
@@ -1044,6 +1044,11 @@ typedef struct wmDrag {
* triggered.
*/
struct wmDropBox *active_dropbox;
+ /* Text to show when the operator poll fails. Typically the message the
+ * operator set with CTX_wm_operator_poll_msg_set(). */
+ const char *disabled_info;
+ bool free_disabled_info;
+
unsigned int flags;
/** List of wmDragIDs, all are guaranteed to have the same ID type. */
diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c
index c60e47a3b0f..3206d0c044e 100644
--- a/source/blender/windowmanager/intern/wm_dragdrop.c
+++ b/source/blender/windowmanager/intern/wm_dragdrop.c
@@ -207,6 +207,30 @@ 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;
+ }
+ }
+
+ /* 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;
@@ -240,6 +264,9 @@ void WM_drag_free(wmDrag *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) {
@@ -277,15 +304,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;
+ }
}
}
}
@@ -309,7 +356,10 @@ static wmDropBox *wm_dropbox_active(bContext *C, wmDrag *drag, const wmEvent *ev
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);
@@ -335,13 +385,36 @@ static void wm_drop_operator_options(bContext *C, wmDrag *drag, const wmEvent *e
}
}
+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 */
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);
}
}
@@ -622,6 +695,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) {
@@ -721,35 +805,42 @@ static void wm_drag_draw_tooltip(bContext *C, wmWindow *win, wmDrag *drag, const
free_tooltip = true;
}
- if (tooltip) {
- const int winsize_y = WM_window_pixels_y(win);
- int x, y;
- if (drag->imb) {
- x = xy[0] - drag->sx / 2;
+ if (!tooltip && !drag->disabled_info) {
+ return;
+ }
- if (xy[1] + drag->sy / 2 + padding + iconsize < winsize_y) {
- y = xy[1] + drag->sy / 2 + padding;
- }
- else {
- y = xy[1] - drag->sy / 2 - padding - iconsize - padding - iconsize;
- }
+ const int winsize_y = WM_window_pixels_y(win);
+ int x, y;
+ if (drag->imb) {
+ x = xy[0] - drag->sx / 2;
+
+ if (xy[1] + drag->sy / 2 + padding + iconsize < winsize_y) {
+ y = xy[1] + drag->sy / 2 + padding;
}
else {
- x = xy[0] - 2 * padding;
+ y = xy[1] - drag->sy / 2 - padding - iconsize - padding - iconsize;
+ }
+ }
+ else {
+ x = xy[0] - 2 * padding;
- if (xy[1] + iconsize + iconsize < winsize_y) {
- y = (xy[1] + iconsize) + padding;
- }
- else {
- y = (xy[1] - iconsize) - padding;
- }
+ if (xy[1] + iconsize + iconsize < winsize_y) {
+ y = (xy[1] + iconsize) + padding;
+ }
+ else {
+ y = (xy[1] - iconsize) - padding;
}
+ }
+ if (tooltip) {
wm_drop_operator_draw(tooltip, x, y);
if (free_tooltip) {
MEM_freeN((void *)tooltip);
}
}
+ else if (drag->disabled_info) {
+ wm_drop_redalert_draw(drag->disabled_info, x, y);
+ }
}
static void wm_drag_draw_default(bContext *C, wmWindow *win, wmDrag *drag, const int xy[2])
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index df976d9a4cd..2aa9a4d8676 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -3055,12 +3055,7 @@ static int wm_handlers_do_intern(bContext *C, wmWindow *win, wmEvent *event, Lis
ListBase *lb = (ListBase *)event->customdata;
LISTBASE_FOREACH_MUTABLE (wmDrag *, drag, lb) {
if (drop->poll(C, drag, event)) {
- /* 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_drop_prepare(C, drag, drop);
/* Pass single matched wmDrag onto the operator. */
BLI_remlink(lb, drag);
@@ -3094,6 +3089,8 @@ static int wm_handlers_do_intern(bContext *C, wmWindow *win, wmEvent *event, Lis
break;
}
}
+ /* Always exit all drags on a drop event, even if poll didn't succeed. */
+ wm_drags_exit(wm, win);
}
}
}
@@ -3390,6 +3387,7 @@ static void wm_event_drag_and_drop_test(wmWindowManager *wm, wmWindow *win, wmEv
screen->do_draw_drag = true;
}
else if (event->type == EVT_ESCKEY) {
+ wm_drags_exit(wm, win);
WM_drag_free_list(&wm->drags);
screen->do_draw_drag = true;
diff --git a/source/blender/windowmanager/wm_event_system.h b/source/blender/windowmanager/wm_event_system.h
index dfc8c578420..40e4d905fcd 100644
--- a/source/blender/windowmanager/wm_event_system.h
+++ b/source/blender/windowmanager/wm_event_system.h
@@ -172,6 +172,8 @@ void wm_tablet_data_from_ghost(const struct GHOST_TabletData *tablet_data, wmTab
/* wm_dropbox.c */
void wm_dropbox_free(void);
+void wm_drags_exit(wmWindowManager *wm, wmWindow *win);
+void wm_drop_prepare(bContext *C, wmDrag *drag, wmDropBox *drop);
void wm_drags_check_ops(bContext *C, const wmEvent *event);
void wm_drags_draw(bContext *C, wmWindow *win);