[Bf-blender-cvs] [ae1dc8f5f98] master: UI: Internal support for custom UI list item drag & activate operators

Julian Eisel noreply at git.blender.org
Thu Jul 15 16:14:26 CEST 2021


Commit: ae1dc8f5f98d045b11312401d6f93d0eae15ddc5
Author: Julian Eisel
Date:   Fri Jul 9 21:46:55 2021 +0200
Branches: master
https://developer.blender.org/rBae1dc8f5f98d045b11312401d6f93d0eae15ddc5

UI: Internal support for custom UI list item drag & activate operators

For pose libraries, we need to be able to apply a pose whenever
activating (clicking) an item in the Pose Library asset view and blend
it by dragging (press & move). And since we want to allow Python scripts
to define what happens at least when activating an asset (so they can
define for example a custom "Apply" operator for preset assets), it
makes sense to just let them pass an operator name to the asset view
template. The template will be introduced in a following commit.

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

M	source/blender/editors/include/UI_interface.h
M	source/blender/editors/interface/interface_handlers.c
M	source/blender/editors/interface/interface_intern.h
M	source/blender/editors/interface/interface_panel.c
M	source/blender/editors/interface/interface_query.c
M	source/blender/editors/interface/interface_templates.c
M	source/blender/makesdna/DNA_screen_types.h
M	source/blender/windowmanager/intern/wm_operators.c

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

diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index 55d0c5c4342..58eaba480e1 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -588,6 +588,8 @@ bool UI_block_is_empty_ex(const uiBlock *block, const bool skip_title);
 bool UI_block_is_empty(const uiBlock *block);
 bool UI_block_can_add_separator(const uiBlock *block);
 
+struct uiList *UI_list_find_mouse_over(const struct ARegion *region, const struct wmEvent *event);
+
 /* interface_region_menu_popup.c */
 /**
  * Popup Menus
@@ -2282,6 +2284,13 @@ void uiTemplateFileSelectPath(uiLayout *layout,
                               struct bContext *C,
                               struct FileSelectParams *params);
 
+struct PointerRNA *UI_list_custom_activate_operator_set(struct uiList *ui_list,
+                                                        const char *opname,
+                                                        bool create_properties);
+struct PointerRNA *UI_list_custom_drag_operator_set(struct uiList *ui_list,
+                                                    const char *opname,
+                                                    bool create_properties);
+
 /* items */
 void uiItemO(uiLayout *layout, const char *name, int icon, const char *opname);
 void uiItemEnumO_ptr(uiLayout *layout,
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index 3136ca64e0f..14fd9025c8f 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -767,23 +767,34 @@ 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 Ownership over \a properties is moved here. The after-func owns it now.
  * \note Can only call while handling buttons.
  */
-PointerRNA *ui_handle_afterfunc_add_operator(wmOperatorType *ot, int opcontext, bool create_props)
+static void ui_handle_afterfunc_add_operator_ex(wmOperatorType *ot,
+                                                PointerRNA **properties,
+                                                int opcontext,
+                                                const uiBut *context_but)
 {
-  PointerRNA *ptr = NULL;
   uiAfterFunc *after = ui_afterfunc_new();
 
   after->optype = ot;
   after->opcontext = opcontext;
+  if (properties) {
+    after->opptr = *properties;
+    *properties = NULL;
+  }
 
-  if (create_props) {
-    ptr = MEM_callocN(sizeof(PointerRNA), __func__);
-    WM_operator_properties_create_ptr(ptr, ot);
-    after->opptr = ptr;
+  if (context_but && context_but->context) {
+    after->context = CTX_store_copy(context_but->context);
   }
+}
 
-  return ptr;
+void ui_handle_afterfunc_add_operator(wmOperatorType *ot, int opcontext)
+{
+  ui_handle_afterfunc_add_operator_ex(ot, NULL, opcontext, NULL);
 }
 
 static void popup_check(bContext *C, wmOperator *op)
@@ -1145,6 +1156,42 @@ static void ui_apply_but_ROW(bContext *C, uiBlock *block, uiBut *but, uiHandleBu
   data->applied = true;
 }
 
+/**
+ * \note Ownership of \a properties is moved here. The after-func owns it now.
+ *
+ * \param context_but: The button to use context from when calling or polling the operator.
+ *
+ * \returns true if the operator was executed, otherwise false.
+ */
+static bool ui_list_invoke_item_operator(bContext *C,
+                                         const uiBut *context_but,
+                                         wmOperatorType *ot,
+                                         PointerRNA **properties)
+{
+  if (!ui_but_context_poll_operator(C, ot, context_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(ot, properties, WM_OP_INVOKE_DEFAULT, context_but);
+  return true;
+}
+
+static void ui_apply_but_LISTROW(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data)
+{
+  uiBut *listbox = ui_list_find_from_row(data->region, but);
+  if (listbox) {
+    uiList *list = listbox->custom_data;
+    if (list && list->dyn_data->custom_activate_optype) {
+      ui_list_invoke_item_operator(
+          C, but, list->dyn_data->custom_activate_optype, &list->dyn_data->custom_activate_opptr);
+    }
+  }
+
+  ui_apply_but_ROW(C, block, but, data);
+}
+
 static void ui_apply_but_TEX(bContext *C, uiBut *but, uiHandleButtonData *data)
 {
   if (!data->str) {
@@ -1617,7 +1664,7 @@ static void ui_drag_toggle_set(bContext *C, uiDragToggleHandle *drag_info, const
    */
   if (drag_info->is_xy_lock_init == false) {
     /* first store the buttons original coords */
-    uiBut *but = ui_but_find_mouse_over_ex(region, xy_input[0], xy_input[1], true);
+    uiBut *but = ui_but_find_mouse_over_ex(region, xy_input[0], xy_input[1], true, NULL, NULL);
 
     if (but) {
       if (but->flag & UI_BUT_DRAG_LOCK) {
@@ -1688,7 +1735,7 @@ static int ui_handler_region_drag_toggle(bContext *C, const wmEvent *event, void
     wmWindow *win = CTX_wm_window(C);
     const ARegion *region = CTX_wm_region(C);
     uiBut *but = ui_but_find_mouse_over_ex(
-        region, drag_info->xy_init[0], drag_info->xy_init[1], true);
+        region, drag_info->xy_init[0], drag_info->xy_init[1], true, NULL, NULL);
 
     if (but) {
       ui_apply_but_undo(but);
@@ -2250,9 +2297,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_DATASETROW:
       ui_apply_but_ROW(C, block, but, data);
       break;
@@ -4284,7 +4333,7 @@ static uiBut *ui_but_list_row_text_activate(bContext *C,
                                             uiButtonActivateType activate_type)
 {
   ARegion *region = CTX_wm_region(C);
-  uiBut *labelbut = ui_but_find_mouse_over_ex(region, event->x, event->y, true);
+  uiBut *labelbut = ui_but_find_mouse_over_ex(region, event->x, event->y, true, NULL, NULL);
 
   if (labelbut && labelbut->type == UI_BTYPE_TEXT && !(labelbut->flag & UI_BUT_DISABLED)) {
     /* exit listrow */
@@ -4778,6 +4827,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->dyn_data->custom_drag_optype) {
+          ret = WM_UI_HANDLER_CONTINUE;
+        }
+      }
       button_activate_state(C, but, BUTTON_STATE_EXIT);
       return ret;
     }
@@ -9289,6 +9347,128 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but)
   return retval;
 }
 
+/**
+ * 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->dyn_data->custom_drag_optype;
+
+  if (do_drag) {
+    const uiBut *hovered_but = ui_but_find_mouse_over(region, event);
+    if (!ui_list_invoke_item_operator(C,
+                                      hovered_but,
+                                      ui_list->dyn_data->custom_drag_optype,
+                                      &ui_list->dyn_data->custom_drag_opptr)) {
+      return WM_UI_HANDLER_CONTINUE;
+    }
+  }
+
+  const int *mouse_xy = ISTWEAK(event->type) ? &event->prevclickx : &event->x;
+  uiBut *listrow = ui_list_row_find_mouse_over(region, mouse_xy[0], mouse_xy[1]);
+  if (listrow) {
+    wmOperatorType *custom_activate_optype = ui_list->dyn_data->custom_activate_optype;
+
+    /* 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)->dyn_data->custom_activate_optype = 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)->dyn_data->custom_activate_optype = custom_activate_optype;
+  }
+
+  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, NULL);
+
+  if (list->dyn_data->custom_drag_optype) {
+    if (ui_but_context_poll_operator(C, list->dyn_data->custom_drag_optype, hovered_but)) {
+      return true;
+    }
+  }
+
+  return (hovered_but && hovered_but->dragpoin);
+}
+
+static int ui_list_handle_

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list