Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/interface/interface_handlers.c
- This file is larger than 256 KB, so syntax highlighting is disabled by default.
| Show First 20 Lines • Show All 761 Lines • ▼ Show 20 Lines | static uiAfterFunc *ui_afterfunc_new(void) | ||||
| return after; | return after; | ||||
| } | } | ||||
| /** | /** | ||||
| * 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 #uiAfterFunc 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) { | |||||
| after->opptr = *properties; | |||||
| *properties = NULL; | |||||
| } | |||||
| if (create_props) { | if (context_but && context_but->context) { | ||||
| ptr = MEM_callocN(sizeof(PointerRNA), __func__); | after->context = CTX_store_copy(context_but->context); | ||||
| WM_operator_properties_create_ptr(ptr, ot); | } | ||||
| after->opptr = ptr; | |||||
| } | } | ||||
| 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) | static void popup_check(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| if (op && op->type->check) { | if (op && op->type->check) { | ||||
| op->type->check(C, op); | op->type->check(C, op); | ||||
| } | } | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 345 Lines • ▼ Show 20 Lines | if (bt != but && bt->poin == but->poin && ELEM(bt->type, UI_BTYPE_ROW, UI_BTYPE_LISTROW)) { | ||||
| ui_but_update_edited(bt); | ui_but_update_edited(bt); | ||||
| } | } | ||||
| } | } | ||||
| data->retval = but->retval; | data->retval = but->retval; | ||||
| data->applied = true; | data->applied = true; | ||||
| } | } | ||||
| /** | |||||
| * \note Ownership of \a properties is moved here. The #uiAfterFunc 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) { | ||||
| return; | return; | ||||
| } | } | ||||
| ui_but_string_set(C, but, data->str); | ui_but_string_set(C, but, data->str); | ||||
| ui_but_update_edited(but); | ui_but_update_edited(but); | ||||
| ▲ Show 20 Lines • Show All 456 Lines • ▼ Show 20 Lines | static void ui_drag_toggle_set(bContext *C, uiDragToggleHandle *drag_info, const int xy_input[2]) | ||||
| /** | /** | ||||
| * Initialize Locking: | * Initialize Locking: | ||||
| * | * | ||||
| * Check if we need to initialize the lock axis by finding if the first | * Check if we need to initialize the lock axis by finding if the first | ||||
| * button we mouse over is X or Y aligned, then lock the mouse to that axis after. | * button we mouse over is X or Y aligned, then lock the mouse to that axis after. | ||||
| */ | */ | ||||
| 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) { | ||||
| const float but_cent_new[2] = { | const float but_cent_new[2] = { | ||||
| BLI_rctf_cent_x(&but->rect), | BLI_rctf_cent_x(&but->rect), | ||||
| BLI_rctf_cent_y(&but->rect), | BLI_rctf_cent_y(&but->rect), | ||||
| }; | }; | ||||
| ▲ Show 20 Lines • Show All 54 Lines • ▼ Show 20 Lines | case MOUSEMOVE: { | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| if (done) { | if (done) { | ||||
| 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); | ||||
| } | } | ||||
| WM_event_remove_ui_handler(&win->modalhandlers, | WM_event_remove_ui_handler(&win->modalhandlers, | ||||
| ui_handler_region_drag_toggle, | ui_handler_region_drag_toggle, | ||||
| ui_handler_region_drag_toggle_remove, | ui_handler_region_drag_toggle_remove, | ||||
| ▲ Show 20 Lines • Show All 545 Lines • ▼ Show 20 Lines | switch (but_type) { | ||||
| case UI_BTYPE_TOGGLE_N: | case UI_BTYPE_TOGGLE_N: | ||||
| case UI_BTYPE_ICON_TOGGLE: | case UI_BTYPE_ICON_TOGGLE: | ||||
| case UI_BTYPE_ICON_TOGGLE_N: | case UI_BTYPE_ICON_TOGGLE_N: | ||||
| case UI_BTYPE_CHECKBOX: | case UI_BTYPE_CHECKBOX: | ||||
| case UI_BTYPE_CHECKBOX_N: | case UI_BTYPE_CHECKBOX_N: | ||||
| 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; | ||||
| case UI_BTYPE_TAB: | case UI_BTYPE_TAB: | ||||
| ui_apply_but_TAB(C, but, data); | ui_apply_but_TAB(C, but, data); | ||||
| break; | break; | ||||
| case UI_BTYPE_SCROLL: | case UI_BTYPE_SCROLL: | ||||
| case UI_BTYPE_GRIP: | case UI_BTYPE_GRIP: | ||||
| ▲ Show 20 Lines • Show All 748 Lines • ▼ Show 20 Lines | if (cursor_data[0] < center) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| return true; | return true; | ||||
| } | } | ||||
| /** | /** | ||||
| * \param x: Screen space cursor location - #wmEvent.x | * \param x: Screen space cursor location - #wmEvent.x | ||||
| * | * | ||||
| * \note ``but->block->aspect`` is used here, so drawing button style is getting scaled too. | * \note `but->block->aspect` is used here, so drawing button style is getting scaled too. | ||||
| */ | */ | ||||
| static void ui_textedit_set_cursor_pos(uiBut *but, uiHandleButtonData *data, const float x) | static void ui_textedit_set_cursor_pos(uiBut *but, uiHandleButtonData *data, const float x) | ||||
| { | { | ||||
| /* XXX pass on as arg. */ | /* XXX pass on as arg. */ | ||||
| uiFontStyle fstyle = UI_style_get()->widget; | uiFontStyle fstyle = UI_style_get()->widget; | ||||
| const float aspect = but->block->aspect; | const float aspect = but->block->aspect; | ||||
| float startx = but->rect.xmin; | float startx = but->rect.xmin; | ||||
| ▲ Show 20 Lines • Show All 436 Lines • ▼ Show 20 Lines | if (but->type == UI_BTYPE_SEARCH_MENU) { | ||||
| ui_searchbox_update(C, data->searchbox, but, true); /* true = reset */ | ui_searchbox_update(C, data->searchbox, but, true); /* true = reset */ | ||||
| } | } | ||||
| /* reset alert flag (avoid confusion, will refresh on exit) */ | /* reset alert flag (avoid confusion, will refresh on exit) */ | ||||
| but->flag &= ~UI_BUT_REDALERT; | but->flag &= ~UI_BUT_REDALERT; | ||||
| ui_but_update(but); | ui_but_update(but); | ||||
| /* Popup blocks don't support moving after creation, so don't change the view for them. */ | |||||
| if (!data->searchbox) { | |||||
| UI_but_ensure_in_view(C, data->region, but); | |||||
| } | |||||
| WM_cursor_modal_set(win, WM_CURSOR_TEXT_EDIT); | WM_cursor_modal_set(win, WM_CURSOR_TEXT_EDIT); | ||||
| #ifdef WITH_INPUT_IME | #ifdef WITH_INPUT_IME | ||||
| if (is_num_but == false && BLT_lang_is_ime_supported()) { | if (is_num_but == false && BLT_lang_is_ime_supported()) { | ||||
| ui_textedit_ime_begin(win, but); | ui_textedit_ime_begin(win, but); | ||||
| } | } | ||||
| #endif | #endif | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 389 Lines • ▼ Show 20 Lines | #ifdef WITH_INPUT_IME | ||||
| && !is_ime_composing && (!WM_event_is_ime_switch(event) || !BLT_lang_is_ime_supported()) | && !is_ime_composing && (!WM_event_is_ime_switch(event) || !BLT_lang_is_ime_supported()) | ||||
| #endif | #endif | ||||
| ) { | ) { | ||||
| char ascii = event->ascii; | char ascii = event->ascii; | ||||
| const char *utf8_buf = event->utf8_buf; | const char *utf8_buf = event->utf8_buf; | ||||
| /* exception that's useful for number buttons, some keyboard | /* exception that's useful for number buttons, some keyboard | ||||
| * numpads have a comma instead of a period */ | * numpads have a comma instead of a period */ | ||||
| if (ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER)) { /* could use data->min*/ | if (ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER)) { /* Could use `data->min`. */ | ||||
| if (event->type == EVT_PADPERIOD && ascii == ',') { | if (event->type == EVT_PADPERIOD && ascii == ',') { | ||||
| ascii = '.'; | ascii = '.'; | ||||
| utf8_buf = NULL; /* force ascii fallback */ | utf8_buf = NULL; /* force ascii fallback */ | ||||
| } | } | ||||
| } | } | ||||
| if (utf8_buf && utf8_buf[0]) { | if (utf8_buf && utf8_buf[0]) { | ||||
| const int utf8_buf_len = BLI_str_utf8_size(utf8_buf); | const int utf8_buf_len = BLI_str_utf8_size(utf8_buf); | ||||
| ▲ Show 20 Lines • Show All 392 Lines • ▼ Show 20 Lines | |||||
| */ | */ | ||||
| static uiBut *ui_but_list_row_text_activate(bContext *C, | static uiBut *ui_but_list_row_text_activate(bContext *C, | ||||
| uiBut *but, | uiBut *but, | ||||
| uiHandleButtonData *data, | uiHandleButtonData *data, | ||||
| const wmEvent *event, | const wmEvent *event, | ||||
| 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 */ | ||||
| data->cancel = true; | data->cancel = true; | ||||
| button_activate_exit(C, but, data, false, false); | button_activate_exit(C, but, data, false, false); | ||||
| /* Activate the text button. */ | /* Activate the text button. */ | ||||
| button_activate_init(C, region, labelbut, activate_type); | button_activate_init(C, region, labelbut, activate_type); | ||||
| ▲ Show 20 Lines • Show All 477 Lines • ▼ Show 20 Lines | |||||
| #endif | #endif | ||||
| if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY) && event->val == KM_PRESS) { | if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY) && event->val == KM_PRESS) { | ||||
| int ret = WM_UI_HANDLER_BREAK; | int ret = WM_UI_HANDLER_BREAK; | ||||
| /* XXX: (a bit ugly) Special case handling for file-browser drag button. */ | /* XXX: (a bit ugly) Special case handling for file-browser drag button. */ | ||||
| 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; | ||||
| } | } | ||||
| } | } | ||||
| else if (data->state == BUTTON_STATE_WAIT_DRAG) { | else if (data->state == BUTTON_STATE_WAIT_DRAG) { | ||||
| /* this function also ends state */ | /* this function also ends state */ | ||||
| if (ui_but_drag_init(C, but, data, event)) { | if (ui_but_drag_init(C, but, data, event)) { | ||||
| ▲ Show 20 Lines • Show All 1,719 Lines • ▼ Show 20 Lines | case UI_GRAD_L_ALT: | ||||
| /* vertical 'value' strip */ | /* vertical 'value' strip */ | ||||
| /* exception only for value strip - use the range set in but->min/max */ | /* exception only for value strip - use the range set in but->min/max */ | ||||
| hsv[2] += ndof->rvec[0] * sensitivity; | hsv[2] += ndof->rvec[0] * sensitivity; | ||||
| CLAMP(hsv[2], hsv_but->but.softmin, hsv_but->but.softmax); | CLAMP(hsv[2], hsv_but->but.softmin, hsv_but->but.softmax); | ||||
| break; | break; | ||||
| default: | default: | ||||
| BLI_assert(!"invalid hsv type"); | BLI_assert_msg(0, "invalid hsv type"); | ||||
| break; | break; | ||||
| } | } | ||||
| if (snap != SNAP_OFF) { | if (snap != SNAP_OFF) { | ||||
| if (ELEM(hsv_but->gradient_type, UI_GRAD_HV, UI_GRAD_HS, UI_GRAD_H)) { | if (ELEM(hsv_but->gradient_type, UI_GRAD_HV, UI_GRAD_HS, UI_GRAD_H)) { | ||||
| ui_color_snap_hue(snap, &hsv[0]); | ui_color_snap_hue(snap, &hsv[0]); | ||||
| } | } | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 1,321 Lines • ▼ Show 20 Lines | if (do_copy) { | ||||
| ui_but_copy(C, but, event->alt); | ui_but_copy(C, but, event->alt); | ||||
| return WM_UI_HANDLER_BREAK; | return WM_UI_HANDLER_BREAK; | ||||
| } | } | ||||
| /* handle menu */ | /* handle menu */ | ||||
| if ((event->type == RIGHTMOUSE) && !IS_EVENT_MOD(event, shift, ctrl, alt, oskey) && | if ((event->type == RIGHTMOUSE) && !IS_EVENT_MOD(event, shift, ctrl, alt, oskey) && | ||||
| (event->val == KM_PRESS)) { | (event->val == KM_PRESS)) { | ||||
| /* RMB has two options now */ | /* RMB has two options now */ | ||||
| if (ui_popup_context_menu_for_button(C, but)) { | if (ui_popup_context_menu_for_button(C, but, event)) { | ||||
| return WM_UI_HANDLER_BREAK; | return WM_UI_HANDLER_BREAK; | ||||
| } | } | ||||
| } | } | ||||
| if (is_disabled) { | if (is_disabled) { | ||||
| return WM_UI_HANDLER_CONTINUE; | return WM_UI_HANDLER_CONTINUE; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 63 Lines • ▼ Show 20 Lines | switch (but->type) { | ||||
| case UI_BTYPE_LISTROW: | case UI_BTYPE_LISTROW: | ||||
| retval = ui_do_but_LISTROW(C, but, data, event); | retval = ui_do_but_LISTROW(C, but, data, event); | ||||
| break; | break; | ||||
| case UI_BTYPE_ROUNDBOX: | case UI_BTYPE_ROUNDBOX: | ||||
| case UI_BTYPE_LABEL: | case UI_BTYPE_LABEL: | ||||
| case UI_BTYPE_IMAGE: | case UI_BTYPE_IMAGE: | ||||
| case UI_BTYPE_PROGRESS_BAR: | case UI_BTYPE_PROGRESS_BAR: | ||||
| case UI_BTYPE_NODE_SOCKET: | case UI_BTYPE_NODE_SOCKET: | ||||
| case UI_BTYPE_PREVIEW_TILE: | |||||
| retval = ui_do_but_EXIT(C, but, data, event); | retval = ui_do_but_EXIT(C, but, data, event); | ||||
| break; | break; | ||||
| case UI_BTYPE_HISTOGRAM: | case UI_BTYPE_HISTOGRAM: | ||||
| retval = ui_do_but_HISTOGRAM(C, block, but, data, event); | retval = ui_do_but_HISTOGRAM(C, block, but, data, event); | ||||
| break; | break; | ||||
| case UI_BTYPE_WAVEFORM: | case UI_BTYPE_WAVEFORM: | ||||
| retval = ui_do_but_WAVEFORM(C, block, but, data, event); | retval = ui_do_but_WAVEFORM(C, block, but, data, event); | ||||
| break; | break; | ||||
| ▲ Show 20 Lines • Show All 878 Lines • ▼ Show 20 Lines | while (region) { | ||||
| } | } | ||||
| else { | else { | ||||
| /* no active button */ | /* no active button */ | ||||
| return; | return; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * In some cases we may want to update the view (#View2D) in-between layout definition and drawing. | |||||
| * E.g. to make sure a button is visible while editing. | |||||
| */ | |||||
| void ui_but_update_view_for_active(const bContext *C, const uiBlock *block) | |||||
| { | |||||
| uiBut *active_but = ui_block_active_but_get(block); | |||||
| if (!active_but || !active_but->active || !active_but->changed || active_but->block != block) { | |||||
| return; | |||||
| } | |||||
| /* If there is a search popup attached to the button, don't change the view. The popups don't | |||||
| * support updating the position to the button position nicely. */ | |||||
| uiHandleButtonData *data = active_but->active; | |||||
| if (data->searchbox) { | |||||
| return; | |||||
| } | |||||
| UI_but_ensure_in_view(C, active_but->active->region, active_but); | |||||
| } | |||||
| /** \} */ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name Button Activation Handling | /** \name Button Activation Handling | ||||
| * \{ */ | * \{ */ | ||||
| static uiBut *ui_but_find_open_event(ARegion *region, const wmEvent *event) | static uiBut *ui_but_find_open_event(ARegion *region, const wmEvent *event) | ||||
| { | { | ||||
| ▲ Show 20 Lines • Show All 448 Lines • ▼ Show 20 Lines | else if (!((event->type == EVT_BUT_CANCEL) && (event->val == 1))) { | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| 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 foreground 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_list_get_increment(const uiList *ui_list, const int type, const int columns) | |||||
| { | |||||
| int increment = 0; | |||||
| /* Handle column offsets for grid layouts. */ | |||||
| if (ELEM(type, EVT_UPARROWKEY, EVT_DOWNARROWKEY) && | |||||
| ELEM(ui_list->layout_type, UILST_LAYOUT_GRID, UILST_LAYOUT_BIG_PREVIEW_GRID)) { | |||||
| increment = (type == EVT_UPARROWKEY) ? -columns : columns; | |||||
| } | |||||
| else { | |||||
| /* Left or right in grid layouts or any direction in single column layouts increments by 1. */ | |||||
| increment = ELEM(type, EVT_UPARROWKEY, EVT_LEFTARROWKEY, WHEELUPMOUSE) ? -1 : 1; | |||||
| } | |||||
| if ((ui_list->filter_sort_flag & UILST_FLT_SORT_REVERSE) != 0) { | |||||
| increment *= -1; | |||||
| } | |||||
| return increment; | |||||
| } | |||||
| 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; | ||||
| int type = event->type, val = event->val; | int type = event->type, val = event->val; | ||||
| int scroll_dir = 1; | int scroll_dir = 1; | ||||
| bool redraw = false; | bool redraw = false; | ||||
| uiList *ui_list = listbox->custom_data; | uiList *ui_list = listbox->custom_data; | ||||
| Show All 17 Lines | if (type == MOUSEPAN) { | ||||
| /* If type still is mouse-pan, we call it handled, since delta-y accumulate. */ | /* If type still is mouse-pan, we call it handled, since delta-y accumulate. */ | ||||
| /* also see wm_event_system.c do_wheel_ui hack */ | /* also see wm_event_system.c do_wheel_ui hack */ | ||||
| if (type == MOUSEPAN) { | if (type == MOUSEPAN) { | ||||
| retval = WM_UI_HANDLER_BREAK; | retval = WM_UI_HANDLER_BREAK; | ||||
| } | } | ||||
| } | } | ||||
| if (val == KM_PRESS) { | if (ELEM(event->type, LEFTMOUSE, EVT_TWEAK_L)) { | ||||
| if ((ELEM(type, EVT_UPARROWKEY, EVT_DOWNARROWKEY) && | retval = ui_list_handle_click_drag(C, ui_list, region, event); | ||||
| } | |||||
| else if (val == KM_PRESS) { | |||||
| if ((ELEM(type, EVT_UPARROWKEY, EVT_DOWNARROWKEY, EVT_LEFTARROWKEY, EVT_RIGHTARROWKEY) && | |||||
| !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 && | ||||
| !IS_EVENT_MOD(event, shift, alt, oskey)))) { | !IS_EVENT_MOD(event, shift, alt, oskey)))) { | ||||
| const int value_orig = RNA_property_int_get(&listbox->rnapoin, listbox->rnaprop); | const int value_orig = RNA_property_int_get(&listbox->rnapoin, listbox->rnaprop); | ||||
| int value, min, max, inc; | int value, min, max; | ||||
| /* activate up/down the list */ | |||||
| value = value_orig; | value = value_orig; | ||||
| if ((ui_list->filter_sort_flag & UILST_FLT_SORT_REVERSE) != 0) { | const int inc = ui_list_get_increment(ui_list, type, dyn_data->columns); | ||||
| inc = ELEM(type, EVT_UPARROWKEY, WHEELUPMOUSE) ? 1 : -1; | |||||
| } | |||||
| else { | |||||
| inc = ELEM(type, EVT_UPARROWKEY, WHEELUPMOUSE) ? -1 : 1; | |||||
| } | |||||
| if (dyn_data->items_filter_neworder || dyn_data->items_filter_flags) { | if (dyn_data->items_filter_neworder || dyn_data->items_filter_flags) { | ||||
| /* If we have a display order different from | /* If we have a display order different from | ||||
| * collection order, we have some work! */ | * collection order, we have some work! */ | ||||
| int *org_order = MEM_mallocN(dyn_data->items_shown * sizeof(int), __func__); | int *org_order = MEM_mallocN(dyn_data->items_shown * sizeof(int), __func__); | ||||
| const int *new_order = dyn_data->items_filter_neworder; | const int *new_order = dyn_data->items_filter_neworder; | ||||
| int org_idx = -1, len = dyn_data->items_len; | int org_idx = -1, len = dyn_data->items_len; | ||||
| int current_idx = -1; | int current_idx = -1; | ||||
| Show All 30 Lines | if ((ELEM(type, EVT_UPARROWKEY, EVT_DOWNARROWKEY, EVT_LEFTARROWKEY, EVT_RIGHTARROWKEY) && | ||||
| } | } | ||||
| CLAMP(value, 0, dyn_data->items_len - 1); | CLAMP(value, 0, dyn_data->items_len - 1); | ||||
| RNA_property_int_range(&listbox->rnapoin, listbox->rnaprop, &min, &max); | RNA_property_int_range(&listbox->rnapoin, listbox->rnaprop, &min, &max); | ||||
| 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; | ||||
| } | } | ||||
| else if (ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE) && event->shift) { | else if (ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE) && event->shift) { | ||||
| /* We now have proper grip, but keep this anyway! */ | /* We now have proper grip, but keep this anyway! */ | ||||
| if (ui_list->list_grip < (dyn_data->visual_height_min - UI_LIST_AUTO_SIZE_THRESHOLD)) { | if (ui_list->list_grip < (dyn_data->visual_height_min - UI_LIST_AUTO_SIZE_THRESHOLD)) { | ||||
| ui_list->list_grip = dyn_data->visual_height; | ui_list->list_grip = dyn_data->visual_height; | ||||
| ▲ Show 20 Lines • Show All 2,116 Lines • Show Last 20 Lines | |||||