[Bf-blender-cvs] [df2e0539353] master: UI: Improved feedback when dropping is not possible on drag 'n drop

Julian Eisel noreply at git.blender.org
Tue Oct 26 18:17:00 CEST 2021


Commit: df2e05393533bff6b14751deb890ef0fc4064c8f
Author: Julian Eisel
Date:   Tue Oct 26 18:04:45 2021 +0200
Branches: master
https://developer.blender.org/rBdf2e05393533bff6b14751deb890ef0fc4064c8f

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

===================================================================

M	source/blender/windowmanager/WM_types.h
M	source/blender/windowmanager/intern/wm_dragdrop.c
M	source/blender/windowmanager/intern/wm_event_system.c
M	source/blender/windowmanager/wm_event_system.h

===================================================================

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);



More information about the Bf-blender-cvs mailing list