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.
This commit is contained in:
committed by
Sybren A. Stüvel
parent
00c7ea68a8
commit
ae1dc8f5f9
@@ -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_is_empty(const uiBlock *block);
|
||||||
bool UI_block_can_add_separator(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 */
|
/* interface_region_menu_popup.c */
|
||||||
/**
|
/**
|
||||||
* Popup Menus
|
* Popup Menus
|
||||||
@@ -2282,6 +2284,13 @@ void uiTemplateFileSelectPath(uiLayout *layout,
|
|||||||
struct bContext *C,
|
struct bContext *C,
|
||||||
struct FileSelectParams *params);
|
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 */
|
/* items */
|
||||||
void uiItemO(uiLayout *layout, const char *name, int icon, const char *opname);
|
void uiItemO(uiLayout *layout, const char *name, int icon, const char *opname);
|
||||||
void uiItemEnumO_ptr(uiLayout *layout,
|
void uiItemEnumO_ptr(uiLayout *layout,
|
||||||
|
|||||||
@@ -767,23 +767,34 @@ static uiAfterFunc *ui_afterfunc_new(void)
|
|||||||
* For executing operators after the button is pressed.
|
* For executing operators after the button is pressed.
|
||||||
* (some non operator buttons need to trigger operators), see: T37795.
|
* (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.
|
* \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();
|
uiAfterFunc *after = ui_afterfunc_new();
|
||||||
|
|
||||||
after->optype = ot;
|
after->optype = ot;
|
||||||
after->opcontext = opcontext;
|
after->opcontext = opcontext;
|
||||||
|
if (properties) {
|
||||||
if (create_props) {
|
after->opptr = *properties;
|
||||||
ptr = MEM_callocN(sizeof(PointerRNA), __func__);
|
*properties = NULL;
|
||||||
WM_operator_properties_create_ptr(ptr, ot);
|
|
||||||
after->opptr = ptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ptr;
|
if (context_but && context_but->context) {
|
||||||
|
after->context = CTX_store_copy(context_but->context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
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;
|
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)
|
static void ui_apply_but_TEX(bContext *C, uiBut *but, uiHandleButtonData *data)
|
||||||
{
|
{
|
||||||
if (!data->str) {
|
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) {
|
if (drag_info->is_xy_lock_init == false) {
|
||||||
/* first store the buttons original coords */
|
/* 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) {
|
||||||
if (but->flag & UI_BUT_DRAG_LOCK) {
|
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);
|
wmWindow *win = CTX_wm_window(C);
|
||||||
const ARegion *region = CTX_wm_region(C);
|
const ARegion *region = CTX_wm_region(C);
|
||||||
uiBut *but = ui_but_find_mouse_over_ex(
|
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) {
|
if (but) {
|
||||||
ui_apply_but_undo(but);
|
ui_apply_but_undo(but);
|
||||||
@@ -2250,9 +2297,11 @@ static void ui_apply_but(
|
|||||||
ui_apply_but_TOG(C, but, data);
|
ui_apply_but_TOG(C, but, data);
|
||||||
break;
|
break;
|
||||||
case UI_BTYPE_ROW:
|
case UI_BTYPE_ROW:
|
||||||
case UI_BTYPE_LISTROW:
|
|
||||||
ui_apply_but_ROW(C, block, but, data);
|
ui_apply_but_ROW(C, block, but, data);
|
||||||
break;
|
break;
|
||||||
|
case UI_BTYPE_LISTROW:
|
||||||
|
ui_apply_but_LISTROW(C, block, but, data);
|
||||||
|
break;
|
||||||
case UI_BTYPE_DATASETROW:
|
case UI_BTYPE_DATASETROW:
|
||||||
ui_apply_but_ROW(C, block, but, data);
|
ui_apply_but_ROW(C, block, but, data);
|
||||||
break;
|
break;
|
||||||
@@ -4284,7 +4333,7 @@ static uiBut *ui_but_list_row_text_activate(bContext *C,
|
|||||||
uiButtonActivateType activate_type)
|
uiButtonActivateType activate_type)
|
||||||
{
|
{
|
||||||
ARegion *region = CTX_wm_region(C);
|
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)) {
|
if (labelbut && labelbut->type == UI_BTYPE_TEXT && !(labelbut->flag & UI_BUT_DISABLED)) {
|
||||||
/* exit listrow */
|
/* 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)) {
|
if (but->dragpoin && but->imb && ui_but_contains_point_px_icon(but, data->region, event)) {
|
||||||
ret = WM_UI_HANDLER_CONTINUE;
|
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);
|
button_activate_state(C, but, BUTTON_STATE_EXIT);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -9289,6 +9347,128 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but)
|
|||||||
return retval;
|
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)
|
static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *region, uiBut *listbox)
|
||||||
{
|
{
|
||||||
int retval = WM_UI_HANDLER_CONTINUE;
|
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) &&
|
if ((ELEM(type, EVT_UPARROWKEY, EVT_DOWNARROWKEY) &&
|
||||||
!IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) ||
|
!IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) ||
|
||||||
((ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE) && event->ctrl &&
|
((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);
|
CLAMP(value, min, max);
|
||||||
|
|
||||||
if (value != value_orig) {
|
if (value != value_orig) {
|
||||||
RNA_property_int_set(&listbox->rnapoin, listbox->rnaprop, value);
|
ui_list_activate_row_from_index(C, region, listbox, ui_list, value);
|
||||||
RNA_property_update(C, &listbox->rnapoin, listbox->rnaprop);
|
|
||||||
|
|
||||||
ui_apply_but_undo(listbox);
|
|
||||||
|
|
||||||
ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM;
|
|
||||||
redraw = true;
|
redraw = true;
|
||||||
}
|
}
|
||||||
retval = WM_UI_HANDLER_BREAK;
|
retval = WM_UI_HANDLER_BREAK;
|
||||||
|
|||||||
@@ -931,9 +931,7 @@ const char *ui_textedit_undo(struct uiUndoStack_Text *undo_stack,
|
|||||||
int *r_cursor_index);
|
int *r_cursor_index);
|
||||||
|
|
||||||
/* interface_handlers.c */
|
/* interface_handlers.c */
|
||||||
PointerRNA *ui_handle_afterfunc_add_operator(struct wmOperatorType *ot,
|
extern void ui_handle_afterfunc_add_operator(struct wmOperatorType *ot, int opcontext);
|
||||||
int opcontext,
|
|
||||||
bool create_props);
|
|
||||||
extern void ui_pan_to_scroll(const struct wmEvent *event, int *type, int *val);
|
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_event(struct bContext *C, struct ARegion *region, uiBut *but);
|
||||||
extern void ui_but_activate_over(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)
|
bool ui_but_contains_point_px(const uiBut *but, const struct ARegion *region, int x, int y)
|
||||||
ATTR_WARN_UNUSED_RESULT;
|
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;
|
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,
|
uiBut *ui_but_find_mouse_over_ex(const struct ARegion *region,
|
||||||
const int x,
|
const int x,
|
||||||
const int y,
|
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,
|
uiBut *ui_but_find_mouse_over(const struct ARegion *region,
|
||||||
const struct wmEvent *event) ATTR_WARN_UNUSED_RESULT;
|
const struct wmEvent *event) ATTR_WARN_UNUSED_RESULT;
|
||||||
uiBut *ui_but_find_rect_over(const struct ARegion *region,
|
uiBut *ui_but_find_rect_over(const struct ARegion *region,
|
||||||
const rcti *rect_px) ATTR_WARN_UNUSED_RESULT;
|
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;
|
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_first(uiBlock *block) ATTR_WARN_UNUSED_RESULT;
|
||||||
uiBut *ui_but_last(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_menu(const uiBlock *block) ATTR_WARN_UNUSED_RESULT;
|
||||||
bool ui_block_is_popover(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;
|
bool ui_block_is_pie_menu(const uiBlock *block) ATTR_WARN_UNUSED_RESULT;
|
||||||
|
|||||||
@@ -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. */
|
/* Initiate edge panning during drags for scrolling beyond the initial region view. */
|
||||||
wmOperatorType *ot = WM_operatortype_find("VIEW2D_OT_edge_pan", true);
|
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) {
|
else if (state == PANEL_STATE_ANIMATION) {
|
||||||
panel_set_flag_recursive(panel, PNL_SELECT, false);
|
panel_set_flag_recursive(panel, PNL_SELECT, false);
|
||||||
|
|||||||
@@ -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);
|
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, ®ion->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... */
|
/* x and y are only used in case event is NULL... */
|
||||||
uiBut *ui_but_find_mouse_over_ex(const ARegion *region,
|
uiBut *ui_but_find_mouse_over_ex(const ARegion *region,
|
||||||
const int x,
|
const int x,
|
||||||
const int y,
|
const int y,
|
||||||
const bool labeledit)
|
const bool labeledit,
|
||||||
|
const uiButFindPollFn find_poll,
|
||||||
|
const void *find_custom_data)
|
||||||
{
|
{
|
||||||
uiBut *butover = NULL;
|
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);
|
ui_window_to_block_fl(region, block, &mx, &my);
|
||||||
|
|
||||||
LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block->buttons) {
|
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 (ui_but_is_interactive(but, labeledit)) {
|
||||||
if (but->pie_dir != UI_RADIAL_NONE) {
|
if (but->pie_dir != UI_RADIAL_NONE) {
|
||||||
if (ui_but_isect_pie_seg(block, but)) {
|
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)
|
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)
|
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;
|
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)) {
|
if (!ui_region_contains_point_px(region, x, y)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -369,11 +390,77 @@ uiBut *ui_list_find_mouse_over_ex(ARegion *region, int x, int y)
|
|||||||
return NULL;
|
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);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
/** \} */
|
/** \} */
|
||||||
|
|
||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
|
|||||||
@@ -5835,6 +5835,15 @@ static void uilist_free_dyn_data(uiList *ui_list)
|
|||||||
return;
|
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_flags);
|
||||||
MEM_SAFE_FREE(dyn_data->items_filter_neworder);
|
MEM_SAFE_FREE(dyn_data->items_filter_neworder);
|
||||||
MEM_SAFE_FREE(dyn_data->customdata);
|
MEM_SAFE_FREE(dyn_data->customdata);
|
||||||
@@ -6848,6 +6857,46 @@ void uiTemplateList(uiLayout *layout,
|
|||||||
NULL);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/** \} */
|
/** \} */
|
||||||
|
|
||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
|
|||||||
@@ -287,6 +287,11 @@ typedef struct uiListDyn {
|
|||||||
int *items_filter_flags;
|
int *items_filter_flags;
|
||||||
/** Org_idx -> new_idx, items_len length. */
|
/** Org_idx -> new_idx, items_len length. */
|
||||||
int *items_filter_neworder;
|
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;
|
} uiListDyn;
|
||||||
|
|
||||||
typedef struct uiList { /* some list UI data need to be saved in file */
|
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_flag;
|
||||||
int filter_sort_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. */
|
/* Custom sub-classes properties. */
|
||||||
IDProperty *properties;
|
IDProperty *properties;
|
||||||
|
|
||||||
|
|||||||
@@ -604,6 +604,12 @@ void WM_operator_properties_create(PointerRNA *ptr, const char *opstring)
|
|||||||
* used for keymaps and macros */
|
* used for keymaps and macros */
|
||||||
void WM_operator_properties_alloc(PointerRNA **ptr, IDProperty **properties, const char *opstring)
|
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) {
|
if (*properties == NULL) {
|
||||||
IDPropertyTemplate val = {0};
|
IDPropertyTemplate val = {0};
|
||||||
*properties = IDP_New(IDP_GROUP, &val, "wmOpItemProp");
|
*properties = IDP_New(IDP_GROUP, &val, "wmOpItemProp");
|
||||||
|
|||||||
Reference in New Issue
Block a user