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:
-rw-r--r--source/blender/editors/include/UI_interface.h9
-rw-r--r--source/blender/editors/interface/interface_handlers.c214
-rw-r--r--source/blender/editors/interface/interface_intern.h26
-rw-r--r--source/blender/editors/interface/interface_panel.c2
-rw-r--r--source/blender/editors/interface/interface_query.c95
-rw-r--r--source/blender/editors/interface/interface_templates.c49
-rw-r--r--source/blender/makesdna/DNA_screen_types.h11
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c6
8 files changed, 382 insertions, 30 deletions
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_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 void ui_list_activate_row_from_index(
+ bContext *C, ARegion *region, uiBut *listbox, uiList *ui_list, int index)
+{
+ uiBut *new_active_row = ui_list_row_find_from_index(region, index, listbox);
+ if (new_active_row) {
+ /* Preferred way to update the active item, also calls the custom activate operator
+ * (#uiList.custom_activate_opname). */
+ UI_but_execute(C, region, new_active_row);
+ }
+ else {
+ /* A bit ugly, set the active index in RNA directly. That's because a button that's
+ * scrolled away in the list box isn't created at all.
+ * The custom activate operator (#uiList.custom_activate_opname) is not called in this case
+ * (which may need the row button context).*/
+ RNA_property_int_set(&listbox->rnapoin, listbox->rnaprop, index);
+ RNA_property_update(C, &listbox->rnapoin, listbox->rnaprop);
+ ui_apply_but_undo(listbox);
+ }
+
+ ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM;
+}
+
static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *region, uiBut *listbox)
{
int retval = WM_UI_HANDLER_CONTINUE;
@@ -9322,7 +9502,10 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi
}
}
- if (val == KM_PRESS) {
+ if (ELEM(event->type, LEFTMOUSE, EVT_TWEAK_L)) {
+ retval = ui_list_handle_click_drag(C, ui_list, region, event);
+ }
+ else if (val == KM_PRESS) {
if ((ELEM(type, EVT_UPARROWKEY, EVT_DOWNARROWKEY) &&
!IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) ||
((ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE) && event->ctrl &&
@@ -9384,12 +9567,7 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi
CLAMP(value, min, max);
if (value != value_orig) {
- RNA_property_int_set(&listbox->rnapoin, listbox->rnaprop, value);
- RNA_property_update(C, &listbox->rnapoin, listbox->rnaprop);
-
- ui_apply_but_undo(listbox);
-
- ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM;
+ ui_list_activate_row_from_index(C, region, listbox, ui_list, value);
redraw = true;
}
retval = WM_UI_HANDLER_BREAK;
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index 40a9c67c630..ab76de8bdba 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -931,9 +931,7 @@ const char *ui_textedit_undo(struct uiUndoStack_Text *undo_stack,
int *r_cursor_index);
/* interface_handlers.c */
-PointerRNA *ui_handle_afterfunc_add_operator(struct wmOperatorType *ot,
- int opcontext,
- bool create_props);
+extern void ui_handle_afterfunc_add_operator(struct wmOperatorType *ot, int opcontext);
extern void ui_pan_to_scroll(const struct wmEvent *event, int *type, int *val);
extern void ui_but_activate_event(struct bContext *C, struct ARegion *region, uiBut *but);
extern void ui_but_activate_over(struct bContext *C, struct ARegion *region, uiBut *but);
@@ -1128,19 +1126,32 @@ bool ui_but_contains_point_px_icon(const uiBut *but,
bool ui_but_contains_point_px(const uiBut *but, const struct ARegion *region, int x, int y)
ATTR_WARN_UNUSED_RESULT;
-uiBut *ui_list_find_mouse_over(struct ARegion *region,
+uiBut *ui_list_find_mouse_over(const struct ARegion *region,
const struct wmEvent *event) ATTR_WARN_UNUSED_RESULT;
-
+uiBut *ui_list_find_from_row(const struct ARegion *region,
+ const uiBut *row_but) ATTR_WARN_UNUSED_RESULT;
+uiBut *ui_list_row_find_mouse_over(const struct ARegion *region,
+ int x,
+ int y) ATTR_WARN_UNUSED_RESULT;
+uiBut *ui_list_row_find_from_index(const struct ARegion *region,
+ const int index,
+ uiBut *listbox) ATTR_WARN_UNUSED_RESULT;
+
+typedef bool (*uiButFindPollFn)(const uiBut *but, const void *customdata);
uiBut *ui_but_find_mouse_over_ex(const struct ARegion *region,
const int x,
const int y,
- const bool labeledit) ATTR_WARN_UNUSED_RESULT;
+ const bool labeledit,
+ const uiButFindPollFn find_poll,
+ const void *find_custom_data) ATTR_WARN_UNUSED_RESULT;
uiBut *ui_but_find_mouse_over(const struct ARegion *region,
const struct wmEvent *event) ATTR_WARN_UNUSED_RESULT;
uiBut *ui_but_find_rect_over(const struct ARegion *region,
const rcti *rect_px) ATTR_WARN_UNUSED_RESULT;
-uiBut *ui_list_find_mouse_over_ex(struct ARegion *region, int x, int y) ATTR_WARN_UNUSED_RESULT;
+uiBut *ui_list_find_mouse_over_ex(const struct ARegion *region,
+ int x,
+ int y) ATTR_WARN_UNUSED_RESULT;
bool ui_but_contains_password(const uiBut *but) ATTR_WARN_UNUSED_RESULT;
@@ -1152,6 +1163,7 @@ uiBut *ui_but_next(uiBut *but) ATTR_WARN_UNUSED_RESULT;
uiBut *ui_but_first(uiBlock *block) ATTR_WARN_UNUSED_RESULT;
uiBut *ui_but_last(uiBlock *block) ATTR_WARN_UNUSED_RESULT;
+uiBut *ui_block_active_but_get(const uiBlock *block);
bool ui_block_is_menu(const uiBlock *block) ATTR_WARN_UNUSED_RESULT;
bool ui_block_is_popover(const uiBlock *block) ATTR_WARN_UNUSED_RESULT;
bool ui_block_is_pie_menu(const uiBlock *block) ATTR_WARN_UNUSED_RESULT;
diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c
index 38dc91fb57f..97d01ac3763 100644
--- a/source/blender/editors/interface/interface_panel.c
+++ b/source/blender/editors/interface/interface_panel.c
@@ -2653,7 +2653,7 @@ static void panel_activate_state(const bContext *C, Panel *panel, const uiHandle
/* Initiate edge panning during drags for scrolling beyond the initial region view. */
wmOperatorType *ot = WM_operatortype_find("VIEW2D_OT_edge_pan", true);
- ui_handle_afterfunc_add_operator(ot, WM_OP_INVOKE_DEFAULT, true);
+ ui_handle_afterfunc_add_operator(ot, WM_OP_INVOKE_DEFAULT);
}
else if (state == PANEL_STATE_ANIMATION) {
panel_set_flag_recursive(panel, PNL_SELECT, false);
diff --git a/source/blender/editors/interface/interface_query.c b/source/blender/editors/interface/interface_query.c
index 025c242d0fc..ecb96a83355 100644
--- a/source/blender/editors/interface/interface_query.c
+++ b/source/blender/editors/interface/interface_query.c
@@ -266,11 +266,29 @@ bool ui_but_contains_point_px_icon(const uiBut *but, ARegion *region, const wmEv
return BLI_rcti_isect_pt(&rect, x, y);
}
+static uiBut *ui_but_find(const ARegion *region,
+ const uiButFindPollFn find_poll,
+ const void *find_custom_data)
+{
+ LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
+ LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block->buttons) {
+ if (find_poll && find_poll(but, find_custom_data) == false) {
+ continue;
+ }
+ return but;
+ }
+ }
+
+ return NULL;
+}
+
/* x and y are only used in case event is NULL... */
uiBut *ui_but_find_mouse_over_ex(const ARegion *region,
const int x,
const int y,
- const bool labeledit)
+ const bool labeledit,
+ const uiButFindPollFn find_poll,
+ const void *find_custom_data)
{
uiBut *butover = NULL;
@@ -282,6 +300,9 @@ uiBut *ui_but_find_mouse_over_ex(const ARegion *region,
ui_window_to_block_fl(region, block, &mx, &my);
LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block->buttons) {
+ if (find_poll && find_poll(but, find_custom_data) == false) {
+ continue;
+ }
if (ui_but_is_interactive(but, labeledit)) {
if (but->pie_dir != UI_RADIAL_NONE) {
if (ui_but_isect_pie_seg(block, but)) {
@@ -310,7 +331,7 @@ uiBut *ui_but_find_mouse_over_ex(const ARegion *region,
uiBut *ui_but_find_mouse_over(const ARegion *region, const wmEvent *event)
{
- return ui_but_find_mouse_over_ex(region, event->x, event->y, event->ctrl != 0);
+ return ui_but_find_mouse_over_ex(region, event->x, event->y, event->ctrl != 0, NULL, NULL);
}
uiBut *ui_but_find_rect_over(const struct ARegion *region, const rcti *rect_px)
@@ -351,7 +372,7 @@ uiBut *ui_but_find_rect_over(const struct ARegion *region, const rcti *rect_px)
return butover;
}
-uiBut *ui_list_find_mouse_over_ex(ARegion *region, int x, int y)
+uiBut *ui_list_find_mouse_over_ex(const ARegion *region, int x, int y)
{
if (!ui_region_contains_point_px(region, x, y)) {
return NULL;
@@ -369,11 +390,77 @@ uiBut *ui_list_find_mouse_over_ex(ARegion *region, int x, int y)
return NULL;
}
-uiBut *ui_list_find_mouse_over(ARegion *region, const wmEvent *event)
+uiBut *ui_list_find_mouse_over(const ARegion *region, const wmEvent *event)
{
+ if (event == NULL) {
+ /* If there is no info about the mouse, just act as if there is nothing underneath it. */
+ return NULL;
+ }
return ui_list_find_mouse_over_ex(region, event->x, event->y);
}
+uiList *UI_list_find_mouse_over(const ARegion *region, const wmEvent *event)
+{
+ uiBut *list_but = ui_list_find_mouse_over(region, event);
+ if (!list_but) {
+ return NULL;
+ }
+
+ return list_but->custom_data;
+}
+
+static bool ui_list_contains_row(const uiBut *listbox_but, const uiBut *listrow_but)
+{
+ BLI_assert(listbox_but->type == UI_BTYPE_LISTBOX);
+ BLI_assert(listrow_but->type == UI_BTYPE_LISTROW);
+ /* The list box and its rows have the same RNA data (active data pointer/prop). */
+ return ui_but_rna_equals(listbox_but, listrow_but);
+}
+
+static bool ui_but_is_listbox_with_row(const uiBut *but, const void *customdata)
+{
+ const uiBut *row_but = customdata;
+ return (but->type == UI_BTYPE_LISTBOX) && ui_list_contains_row(but, row_but);
+}
+
+uiBut *ui_list_find_from_row(const ARegion *region, const uiBut *row_but)
+{
+ return ui_but_find(region, ui_but_is_listbox_with_row, row_but);
+}
+
+static bool ui_but_is_listrow(const uiBut *but, const void *UNUSED(customdata))
+{
+ return but->type == UI_BTYPE_LISTROW;
+}
+
+uiBut *ui_list_row_find_mouse_over(const ARegion *region, const int x, const int y)
+{
+ return ui_but_find_mouse_over_ex(region, x, y, false, ui_but_is_listrow, NULL);
+}
+
+struct ListRowFindIndexData {
+ int index;
+ uiBut *listbox;
+};
+
+static bool ui_but_is_listrow_at_index(const uiBut *but, const void *customdata)
+{
+ const struct ListRowFindIndexData *find_data = customdata;
+
+ return ui_but_is_listrow(but, NULL) && ui_list_contains_row(find_data->listbox, but) &&
+ (but->hardmax == find_data->index);
+}
+
+uiBut *ui_list_row_find_from_index(const ARegion *region, const int index, uiBut *listbox)
+{
+ BLI_assert(listbox->type == UI_BTYPE_LISTBOX);
+ struct ListRowFindIndexData data = {
+ .index = index,
+ .listbox = listbox,
+ };
+ return ui_but_find(region, ui_but_is_listrow_at_index, &data);
+}
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 9fbaa62b77f..548852b407e 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -5835,6 +5835,15 @@ static void uilist_free_dyn_data(uiList *ui_list)
return;
}
+ if (dyn_data->custom_activate_opptr) {
+ WM_operator_properties_free(dyn_data->custom_activate_opptr);
+ MEM_freeN(dyn_data->custom_activate_opptr);
+ }
+ if (dyn_data->custom_drag_opptr) {
+ WM_operator_properties_free(dyn_data->custom_drag_opptr);
+ MEM_freeN(dyn_data->custom_drag_opptr);
+ }
+
MEM_SAFE_FREE(dyn_data->items_filter_flags);
MEM_SAFE_FREE(dyn_data->items_filter_neworder);
MEM_SAFE_FREE(dyn_data->customdata);
@@ -6848,6 +6857,46 @@ void uiTemplateList(uiLayout *layout,
NULL);
}
+/**
+ * \return: A RNA pointer for the operator properties.
+ */
+PointerRNA *UI_list_custom_activate_operator_set(uiList *ui_list,
+ const char *opname,
+ bool create_properties)
+{
+ uiListDyn *dyn_data = ui_list->dyn_data;
+ dyn_data->custom_activate_optype = WM_operatortype_find(opname, false);
+ if (!dyn_data->custom_activate_optype) {
+ return NULL;
+ }
+
+ if (create_properties) {
+ WM_operator_properties_alloc(&dyn_data->custom_activate_opptr, NULL, opname);
+ }
+
+ return dyn_data->custom_activate_opptr;
+}
+
+/**
+ * \return: A RNA pointer for the operator properties.
+ */
+PointerRNA *UI_list_custom_drag_operator_set(uiList *ui_list,
+ const char *opname,
+ bool create_properties)
+{
+ uiListDyn *dyn_data = ui_list->dyn_data;
+ dyn_data->custom_drag_optype = WM_operatortype_find(opname, false);
+ if (!dyn_data->custom_drag_optype) {
+ return NULL;
+ }
+
+ if (create_properties) {
+ WM_operator_properties_alloc(&dyn_data->custom_drag_opptr, NULL, opname);
+ }
+
+ return dyn_data->custom_drag_opptr;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h
index 79eca09b80a..7032bc23b2c 100644
--- a/source/blender/makesdna/DNA_screen_types.h
+++ b/source/blender/makesdna/DNA_screen_types.h
@@ -287,6 +287,11 @@ typedef struct uiListDyn {
int *items_filter_flags;
/** Org_idx -> new_idx, items_len length. */
int *items_filter_neworder;
+
+ struct wmOperatorType *custom_drag_optype;
+ struct PointerRNA *custom_drag_opptr;
+ struct wmOperatorType *custom_activate_optype;
+ struct PointerRNA *custom_activate_opptr;
} uiListDyn;
typedef struct uiList { /* some list UI data need to be saved in file */
@@ -313,6 +318,12 @@ typedef struct uiList { /* some list UI data need to be saved in file */
int filter_flag;
int filter_sort_flag;
+ /** Operator executed when activating an item. */
+ const char *custom_activate_opname;
+ /** Operator executed when dragging an item (item gets activated too, without running
+ * custom_activate_opname above). */
+ const char *custom_drag_opname;
+
/* Custom sub-classes properties. */
IDProperty *properties;
diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c
index 485e65cf3dc..576f15731a0 100644
--- a/source/blender/windowmanager/intern/wm_operators.c
+++ b/source/blender/windowmanager/intern/wm_operators.c
@@ -604,6 +604,12 @@ void WM_operator_properties_create(PointerRNA *ptr, const char *opstring)
* used for keymaps and macros */
void WM_operator_properties_alloc(PointerRNA **ptr, IDProperty **properties, const char *opstring)
{
+ IDProperty *tmp_properties = NULL;
+ /* Allow passing NULL for properties, just create the properties here then. */
+ if (properties == NULL) {
+ properties = &tmp_properties;
+ }
+
if (*properties == NULL) {
IDPropertyTemplate val = {0};
*properties = IDP_New(IDP_GROUP, &val, "wmOpItemProp");