[Bf-blender-cvs] [01a5d3fa282] ui-asset-view-template: Support passing operator to invoke on activating or dragging an item

Julian Eisel noreply at git.blender.org
Wed Mar 24 14:36:10 CET 2021


Commit: 01a5d3fa282e948328efb77f616e41474157ab2f
Author: Julian Eisel
Date:   Wed Mar 24 13:43:17 2021 +0100
Branches: ui-asset-view-template
https://developer.blender.org/rB01a5d3fa282e948328efb77f616e41474157ab2f

Support passing operator to invoke on activating or dragging an item

An annoyance is that the operator names have to be passed to the template which
makes the already long argument list even longer.
Thought about a couple of ways to do this, unfortunately this is the only
decent way to do this that I see. There's also no way to pass operator options
currently.

Getting the handling to work correctly took some effort, and the code is not
exactly nice.

Note that even though we activate the item when calling the custom drag
operator (helps indicating the pose being blended for example), we only call
the drag operator then, not the activate one. They are never executed both.

Also note that this won't compile right now, I'll have to commit some changes
to master first.

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

M	source/blender/editors/include/UI_interface.h
M	source/blender/editors/interface/interface_handlers.c
M	source/blender/editors/interface/interface_template_asset_view.cc
M	source/blender/makesdna/DNA_screen_types.h
M	source/blender/makesrna/intern/rna_ui_api.c

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

diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index 201f0386b21..21ff7532999 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -2233,7 +2233,9 @@ void uiTemplateAssetView(struct uiLayout *layout,
                          const char *assets_propname,
                          struct PointerRNA *active_dataptr,
                          const char *active_propname,
-                         const struct AssetFilterSettings *filter_settings);
+                         const struct AssetFilterSettings *filter_settings,
+                         const char *activate_opname,
+                         const char *drag_opname);
 
 /* items */
 void uiItemO(uiLayout *layout, const char *name, int icon, const char *opname);
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index 5990d0097e8..5b28a13eb6c 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -714,15 +714,24 @@ static uiAfterFunc *ui_afterfunc_new(void)
  * For executing operators after the button is pressed.
  * (some non operator buttons need to trigger operators), see: T37795.
  *
+ * \param context_but: A button from which to get the context from (`uiBut.context`) for the
+ *                     operator execution.
+ *
  * \note Can only call while handling buttons.
  */
-PointerRNA *ui_handle_afterfunc_add_operator(wmOperatorType *ot, int opcontext, bool create_props)
+static PointerRNA *ui_handle_afterfunc_add_operator_ex(wmOperatorType *ot,
+                                                       int opcontext,
+                                                       bool create_props,
+                                                       const uiBut *context_but)
 {
   PointerRNA *ptr = NULL;
   uiAfterFunc *after = ui_afterfunc_new();
 
   after->optype = ot;
   after->opcontext = opcontext;
+  if (context_but && context_but->context) {
+    after->context = CTX_store_copy(context_but->context);
+  }
 
   if (create_props) {
     ptr = MEM_callocN(sizeof(PointerRNA), __func__);
@@ -733,6 +742,11 @@ PointerRNA *ui_handle_afterfunc_add_operator(wmOperatorType *ot, int opcontext,
   return ptr;
 }
 
+PointerRNA *ui_handle_afterfunc_add_operator(wmOperatorType *ot, int opcontext, bool create_props)
+{
+  return ui_handle_afterfunc_add_operator_ex(ot, opcontext, create_props, NULL);
+}
+
 static void popup_check(bContext *C, wmOperator *op)
 {
   if (op && op->type->check) {
@@ -1057,6 +1071,43 @@ static void ui_apply_but_ROW(bContext *C, uiBlock *block, uiBut *but, uiHandleBu
   data->applied = true;
 }
 
+/**
+ * \returns true if the operator was executed, otherwise false.
+ */
+static bool ui_list_invoke_item_operator(bContext *C,
+                                         const ARegion *region,
+                                         const wmEvent *event,
+                                         const char *name)
+{
+  wmOperatorType *drag_ot = WM_operatortype_find(name, false);
+  const uiBut *hovered_but = ui_but_find_mouse_over(region, event);
+
+  if (!ui_but_context_poll_operator(C, drag_ot, hovered_but)) {
+    return false;
+  }
+
+  /* Allow the context to be set from the hovered button, so the list item draw callback can set
+   * context for the operators. */
+  ui_handle_afterfunc_add_operator_ex(drag_ot, WM_OP_INVOKE_DEFAULT, false, hovered_but);
+  return true;
+}
+
+static void ui_apply_but_LISTROW(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data)
+{
+  wmWindow *window = CTX_wm_window(C);
+
+  uiBut *listbox = ui_list_find_mouse_over(data->region, window->eventstate);
+  if (listbox) {
+    uiList *list = listbox->custom_data;
+    if (list && list->custom_activate_opname) {
+      ui_list_invoke_item_operator(
+          C, data->region, window->eventstate, list->custom_activate_opname);
+    }
+  }
+
+  ui_apply_but_ROW(C, block, but, data);
+}
+
 static void ui_apply_but_TEX(bContext *C, uiBut *but, uiHandleButtonData *data)
 {
   if (!data->str) {
@@ -2158,9 +2209,11 @@ static void ui_apply_but(
       ui_apply_but_TOG(C, but, data);
       break;
     case UI_BTYPE_ROW:
-    case UI_BTYPE_LISTROW:
       ui_apply_but_ROW(C, block, but, data);
       break;
+    case UI_BTYPE_LISTROW:
+      ui_apply_but_LISTROW(C, block, but, data);
+      break;
     case UI_BTYPE_TAB:
       ui_apply_but_TAB(C, but, data);
       break;
@@ -4659,6 +4712,15 @@ static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, con
       if (but->dragpoin && but->imb && ui_but_contains_point_px_icon(but, data->region, event)) {
         ret = WM_UI_HANDLER_CONTINUE;
       }
+      /* Same special case handling for UI lists. Return CONTINUE so that a tweak or CLICK event
+       * will be sent for the list to work with. */
+      const uiBut *listbox = ui_list_find_mouse_over(data->region, event);
+      if (listbox) {
+        const uiList *ui_list = listbox->custom_data;
+        if (ui_list && ui_list->custom_drag_opname) {
+          ret = WM_UI_HANDLER_CONTINUE;
+        }
+      }
       button_activate_state(C, but, BUTTON_STATE_EXIT);
       return ret;
     }
@@ -9023,6 +9085,104 @@ static bool ui_but_is_listrow(const uiBut *but)
   return but->type == UI_BTYPE_LISTROW;
 }
 
+/**
+ * Activate the underlying list-row button, so the row is highlighted.
+ * Early exits if \a activate_dragging is true, but the custom drag operator fails to execute.
+ * Gives the wanted behavior where the item is activated on a tweak event when the custom drag
+ * operator is executed.
+ */
+static int ui_list_activate_hovered_row(bContext *C,
+                                        ARegion *region,
+                                        const uiList *ui_list,
+                                        const wmEvent *event,
+                                        bool activate_dragging)
+{
+  const bool do_drag = activate_dragging && ui_list->custom_drag_opname;
+
+  if (do_drag) {
+    if (!ui_list_invoke_item_operator(C, region, event, ui_list->custom_drag_opname)) {
+      return WM_UI_HANDLER_CONTINUE;
+    }
+  }
+
+  const int *mouse_xy = ISTWEAK(event->type) ? &event->prevclickx : &event->x;
+  uiBut *listrow = ui_but_find_mouse_over_ex(
+      region, mouse_xy[0], mouse_xy[1], false, ui_but_is_listrow);
+  if (listrow) {
+    const char *custom_activate_opname = ui_list->custom_activate_opname;
+
+    /* Hacky: Ensure the custom activate operator is not called when the custom drag operator was.
+     * Only one should run! */
+    if (activate_dragging && do_drag) {
+      ((uiList *)ui_list)->custom_activate_opname = NULL;
+    }
+
+    /* Simulate click on listrow button itself (which may be overlapped by another button). Also
+     * calls the custom activate operator (ui_list->custom_activate_opname). */
+    UI_but_execute(C, region, listrow);
+
+    ((uiList *)ui_list)->custom_activate_opname = custom_activate_opname;
+  }
+
+  return WM_UI_HANDLER_BREAK;
+}
+
+static bool ui_list_is_hovering_draggable_but(bContext *C,
+                                              const uiList *list,
+                                              const ARegion *region,
+                                              const wmEvent *event)
+{
+  /* On a tweak event, uses the coordinates from where tweaking was started. */
+  const int *mouse_xy = ISTWEAK(event->type) ? &event->prevclickx : &event->x;
+  const uiBut *hovered_but = ui_but_find_mouse_over_ex(
+      region, mouse_xy[0], mouse_xy[1], false, NULL);
+
+  if (list->custom_drag_opname) {
+    wmOperatorType *drag_ot = WM_operatortype_find(list->custom_drag_opname, false);
+    if (ui_but_context_poll_operator(C, drag_ot, hovered_but)) {
+      return true;
+    }
+  }
+
+  return (hovered_but && hovered_but->dragpoin);
+}
+
+static int ui_list_handle_click_drag(bContext *C,
+                                     const uiList *ui_list,
+                                     ARegion *region,
+                                     const wmEvent *event)
+{
+  if (!ELEM(event->type, LEFTMOUSE, EVT_TWEAK_L)) {
+    return WM_HANDLER_CONTINUE;
+  }
+
+  int retval = WM_HANDLER_CONTINUE;
+
+  const bool is_draggable = ui_list_is_hovering_draggable_but(C, ui_list, region, event);
+  bool activate = false;
+  bool activate_dragging = false;
+
+  if (event->type == EVT_TWEAK_L) {
+    if (is_draggable) {
+      activate_dragging = true;
+      activate = true;
+    }
+  }
+  /* KM_CLICK is only sent after an uncaught release event, so the forground button gets all
+   * regular events (including mouse presses to start dragging) and this part only kicks in if it
+   * hasn't handled the release event. Note that if there's no overlaid button, the row selects
+   * on the press event already via regular UI_BTYPE_LISTROW handling. */
+  else if ((event->type == LEFTMOUSE) && (event->val == KM_CLICK)) {
+    activate = true;
+  }
+
+  if (activate) {
+    retval = ui_list_activate_hovered_row(C, region, ui_list, event, activate_dragging);
+  }
+
+  return retval;
+}
+
 static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *region, uiBut *listbox)
 {
   int retval = WM_UI_HANDLER_CONTINUE;
@@ -9056,21 +9216,8 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi
     }
   }
 
-  /* Pass selection to the underlying listrow button if the foreground button didn't catch it.
-   * KM_CLICK is only sent after an uncaught release event, so the forground button gets all
-   * regular events (including mouse presses to start dragging) and this part only kicks in if it
-   * hasn't handled the release event. Note that if there's no overlaid button, the row selects on
-   * the press event already via regular UI_BTYPE_LISTROW handling. */
-  if (ELEM(type, LEFTMOUSE) && (val == KM_CLICK)) {
-    uiBut *listrow = ui_but_find_mouse_over_ex(
-        region, event->x, event->y, false, ui_but_is_listrow);
-
-    if (listrow) {
-      /* Simulate click on listrow button itself (which may be overlapped by another button). */

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list