Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/interface/interface_handlers.c
| Context not available. | |||||
| /* a simple version of uiHandleButtonData when accessing multiple buttons */ | /* a simple version of uiHandleButtonData when accessing multiple buttons */ | ||||
| typedef struct uiButMultiState { | typedef struct uiButMultiState { | ||||
| double origvalue; | double origvalue, value; | ||||
| /* x position of the cursor at the moment softmin/softmax is reached | |||||
| * for proportional multi-number editing (value ladders only) */ | |||||
| int drag_thresh_x; | |||||
| uiBut *but; | uiBut *but; | ||||
| } uiButMultiState; | } uiButMultiState; | ||||
| Context not available. | |||||
| mbut_state = MEM_callocN(sizeof(*mbut_state), __func__); | mbut_state = MEM_callocN(sizeof(*mbut_state), __func__); | ||||
| mbut_state->but = but; | mbut_state->but = but; | ||||
| mbut_state->origvalue = ui_get_but_val(but); | mbut_state->origvalue = ui_get_but_val(but); | ||||
| mbut_state->value = mbut_state->origvalue; | |||||
| BLI_linklist_prepend(&data->multi_data.mbuts, mbut_state); | BLI_linklist_prepend(&data->multi_data.mbuts, mbut_state); | ||||
| Context not available. | |||||
| return changed; | return changed; | ||||
| } | } | ||||
| static void ui_multibut_states_create(uiBut *but_active, uiHandleButtonData *data) | void ui_multibut_states_create(uiBut *but_active, uiHandleButtonData *data) | ||||
| { | { | ||||
| uiBut *but; | uiBut *but; | ||||
| Context not available. | |||||
| /* ************* number editing for various types ************* */ | /* ************* number editing for various types ************* */ | ||||
| static void ui_numedit_begin(uiBut *but, uiHandleButtonData *data) | void ui_numedit_begin(uiBut *but, uiHandleButtonData *data) | ||||
| { | { | ||||
| if (but->type == BUT_CURVE) { | if (but->type == BUT_CURVE) { | ||||
| but->editcumap = (CurveMapping *)but->poin; | but->editcumap = (CurveMapping *)but->poin; | ||||
| Context not available. | |||||
| button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); | button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); | ||||
| retval = WM_UI_HANDLER_BREAK; | retval = WM_UI_HANDLER_BREAK; | ||||
| } | } | ||||
| else if (ELEM(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->alt) { | |||||
| but->flag |= UI_BUT_VLADDER_OPEN; | |||||
| button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); | |||||
| retval = WM_UI_HANDLER_BREAK; | |||||
| } | |||||
| else if (event->type == LEFTMOUSE) { | else if (event->type == LEFTMOUSE) { | ||||
| data->dragstartx = data->draglastx = ui_is_a_warp_but(but) ? screen_mx : mx; | data->dragstartx = data->draglastx = ui_is_a_warp_but(but) ? screen_mx : mx; | ||||
| button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); | button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); | ||||
| Context not available. | |||||
| if (event->shift) fac /= 10.0f; | if (event->shift) fac /= 10.0f; | ||||
| if (event->alt) fac /= 20.0f; | if (event->alt) fac /= 20.0f; | ||||
| if (ui_numedit_but_NUM(but, data, (ui_is_a_warp_but(but) ? screen_mx : mx), snap, fac)) | if (but->flag & UI_BUT_VLADDER_OPEN) {} /* skip... */ | ||||
| else if (ui_numedit_but_NUM(but, data, (ui_is_a_warp_but(but) ? screen_mx : mx), snap, fac)) | |||||
| ui_numedit_apply(C, block, but, data); | ui_numedit_apply(C, block, but, data); | ||||
| #ifdef USE_DRAG_MULTINUM | #ifdef USE_DRAG_MULTINUM | ||||
| else if (data->multi_data.has_mbuts) { | else if (data->multi_data.has_mbuts) { | ||||
| Context not available. | |||||
| button_activate_state(C, but, BUTTON_STATE_EXIT); | button_activate_state(C, but, BUTTON_STATE_EXIT); | ||||
| } | } | ||||
| else if (but->flag & UI_BUT_VLADDER_OPEN) | |||||
| ui_vladder_create(C, but); | |||||
| else { | else { | ||||
| button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); | button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); | ||||
| } | } | ||||
| Context not available. | |||||
| button_activate_state(C, but, BUTTON_STATE_EXIT); | button_activate_state(C, but, BUTTON_STATE_EXIT); | ||||
| } | } | ||||
| else if (but->flag & UI_BUT_VLADDER_OPEN) | |||||
| ui_vladder_create(C, but); | |||||
| else { | else { | ||||
| button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); | button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); | ||||
| } | } | ||||
| Context not available. | |||||
| button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); | button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); | ||||
| retval = WM_UI_HANDLER_BREAK; | retval = WM_UI_HANDLER_BREAK; | ||||
| } | } | ||||
| else if (ELEM(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->alt) { | |||||
| but->flag |= UI_BUT_VLADDER_OPEN; | |||||
| button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); | |||||
| retval = WM_UI_HANDLER_BREAK; | |||||
| } | |||||
| #if 0 | |||||
| /* Conflicts with value ladders. UI-team decided to break it but leave it commented for now. | |||||
| * XXX Delete it if no one complains or sends bug report until 2.75! | |||||
| * - Severin - */ | |||||
| /* alt-click on sides to get "arrows" like in NUM buttons, and match wheel usage above */ | /* alt-click on sides to get "arrows" like in NUM buttons, and match wheel usage above */ | ||||
| else if (event->type == LEFTMOUSE && event->alt) { | else if (event->type == LEFTMOUSE && event->alt) { | ||||
| int halfpos = BLI_rctf_cent_x(&but->rect); | int halfpos = BLI_rctf_cent_x(&but->rect); | ||||
| Context not available. | |||||
| else | else | ||||
| mx = but->rect.xmax; | mx = but->rect.xmax; | ||||
| } | } | ||||
| #endif | |||||
| else if (event->type == LEFTMOUSE) { | else if (event->type == LEFTMOUSE) { | ||||
| data->dragstartx = mx; | data->dragstartx = mx; | ||||
| data->draglastx = mx; | data->draglastx = mx; | ||||
| Context not available. | |||||
| data->multi_data.drag_dir[0] += abs(data->draglastx - mx); | data->multi_data.drag_dir[0] += abs(data->draglastx - mx); | ||||
| data->multi_data.drag_dir[1] += abs(data->draglasty - my); | data->multi_data.drag_dir[1] += abs(data->draglasty - my); | ||||
| #endif | #endif | ||||
| if (ui_numedit_but_SLI(but, data, mx, true, event->ctrl != 0, event->shift != 0)) | if (but->flag & UI_BUT_VLADDER_OPEN) {} /* skip... */ | ||||
| else if (ui_numedit_but_SLI(but, data, mx, true, event->ctrl != 0, event->shift != 0)) | |||||
| ui_numedit_apply(C, block, but, data); | ui_numedit_apply(C, block, but, data); | ||||
| #ifdef USE_DRAG_MULTINUM | #ifdef USE_DRAG_MULTINUM | ||||
| Context not available. | |||||
| else | else | ||||
| data->cancel = true; | data->cancel = true; | ||||
| } | } | ||||
| button_activate_state(C, but, BUTTON_STATE_EXIT); | button_activate_state(C, but, BUTTON_STATE_EXIT); | ||||
| retval = WM_UI_HANDLER_BREAK; | retval = WM_UI_HANDLER_BREAK; | ||||
| } | } | ||||
| else { | else { | ||||
| /* edit the value directly */ | if (but->flag & UI_BUT_VLADDER_OPEN) | ||||
| button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); | ui_vladder_create(C, but); | ||||
| else | |||||
| /* edit the value directly */ | |||||
| button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); | |||||
| retval = WM_UI_HANDLER_BREAK; | retval = WM_UI_HANDLER_BREAK; | ||||
| } | } | ||||
| } | } | ||||
| Context not available. | |||||
| ui_textedit_end(C, but, data); | ui_textedit_end(C, but, data); | ||||
| /* number editing */ | /* number editing */ | ||||
| if (state == BUTTON_STATE_NUM_EDITING) { | if (state == BUTTON_STATE_NUM_EDITING && !(but->flag & UI_BUT_VLADDER_OPEN)) { | ||||
| if (ui_is_a_warp_but(but)) | if (ui_is_a_warp_but(but)) | ||||
| WM_cursor_grab_enable(CTX_wm_window(C), true, true, NULL); | WM_cursor_grab_enable(CTX_wm_window(C), true, true, NULL); | ||||
| ui_numedit_begin(but, data); | ui_numedit_begin(but, data); | ||||
| Context not available. | |||||
| retval = WM_UI_HANDLER_CONTINUE; | retval = WM_UI_HANDLER_CONTINUE; | ||||
| if (block->flag & UI_BLOCK_VLADDER) | |||||
| return retval; | |||||
| mx = event->x; | mx = event->x; | ||||
| my = event->y; | my = event->y; | ||||
| ui_window_to_block(ar, block, &mx, &my); | ui_window_to_block(ar, block, &mx, &my); | ||||
| Context not available. | |||||
| return retval; | return retval; | ||||
| } | } | ||||
| /************************** Value Ladders ***************************/ | |||||
| /* | |||||
| * Note: | |||||
| * - Since menu handling is skipped for ladders, we have to re-enable popup dragging and multinumber editing | |||||
| * - To prevent oversized value ladders we only support stepping values from 0.0001 to 1000 for now | |||||
| */ | |||||
| static void ui_vladder_remove(bContext *C, uiVLadderData *data) | |||||
| { | |||||
| uiBut *but = data->but, *mbut; | |||||
| uiHandleButtonData *hbdata = but->active; | |||||
| #ifdef USE_DRAG_MULTINUM | |||||
| if (hbdata->multi_data.has_mbuts) { | |||||
| uiBlock *block = but->block; | |||||
| for (mbut = block->buttons.first; mbut; mbut = mbut->next) { | |||||
| if (mbut->flag & UI_BUT_DRAG_MULTI) { | |||||
| mbut->flag &= ~UI_BUT_DRAG_MULTI; | |||||
| } | |||||
| } | |||||
| ui_multibut_free(hbdata, block); | |||||
| } | |||||
| #endif | |||||
| uiButClearFlag(but, UI_SELECT | UI_ACTIVE); | |||||
| button_activate_state(C, but, BUTTON_STATE_EXIT); | |||||
| uiPupBlockClose(C, data->block); | |||||
| WM_event_remove_ui_handler(&hbdata->window->modalhandlers, ui_vladder_handle, NULL, data, true); | |||||
| WM_cursor_grab_disable(but->active->window, NULL); /* just in case */ | |||||
| WM_event_add_mousemove(C); | |||||
| MEM_freeN(data); | |||||
| } | |||||
| static void ui_vladder_handle_numedit(bContext *C, const wmEvent *event, uiVLadderData *data) | |||||
| { | |||||
| uiBut *but = data->but; | |||||
| uiHandleButtonData *hbdata = but->active; | |||||
| const float fac = event->shift ? 0.02f : 0.25f; | |||||
| float value = hbdata->value, val_step = data->val_step[data->step_active]; | |||||
| int mx = event->x, mx_prev = event->prevx; | |||||
| const bool incr = mx > mx_prev; | |||||
| if (mx == mx_prev || abs(mx - event->prevclickx) <= 3) /* nothing to do here */ | |||||
| return; | |||||
| mx *= fac; | |||||
| mx_prev *= fac; | |||||
| if (ui_is_but_unit(but)) { | |||||
| UnitSettings *unit = but->block->unit; | |||||
| float fac_unit = 1.0f; | |||||
| int unit_type = RNA_SUBTYPE_UNIT_VALUE(uiButGetUnitType(but)); | |||||
| if (bUnit_IsValid(unit->system, unit_type)) { | |||||
| fac_unit = (float)bUnit_BaseScalar(unit->system, unit_type); | |||||
| if (ELEM(unit_type, B_UNIT_LENGTH, B_UNIT_AREA, B_UNIT_VOLUME)) { | |||||
| fac_unit /= unit->scale_length; | |||||
| } | |||||
| } | |||||
| val_step *= fac_unit; | |||||
| } | |||||
| if (abs(mx - mx_prev) & 1) { /* halves the speed -> more control */ | |||||
| const float softmin = but->softmin, softmax = but->softmax; | |||||
| if (incr) { | |||||
| value += val_step; | |||||
| if (value > softmax) | |||||
| value = softmax; | |||||
| } | |||||
| else { | |||||
| value -= val_step; | |||||
| if (value < softmin) | |||||
| value = softmin; | |||||
| } | |||||
| } | |||||
| if (value != hbdata->value) { | |||||
| hbdata->value = value; | |||||
| /* If space is rare it may happen that the popup region jitters when 'ui_apply_but_funcs_after' updates the | |||||
| * RNA_properties. I wasn't able to do anything else than minimizing the jittering | |||||
| * - Severin - */ | |||||
| ui_numedit_apply(C, but->block, but, hbdata); | |||||
| #ifdef USE_DRAG_MULTINUM | |||||
| if (but->active->multi_data.has_mbuts) { | |||||
| for (but = but->block->buttons.first; but; but = but->next) { | |||||
| if (but->flag & UI_BUT_DRAG_MULTI) { | |||||
| uiButMultiState *mbut_state = ui_multibut_lookup(hbdata, but); | |||||
| const float softmin = but->softmin, softmax = but->softmax; | |||||
| /* ui_numedit_apply doesn't work in all situations, here */ | |||||
| if (!hbdata->multi_data.is_proportional) { | |||||
| int thresh_mx = mbut_state->drag_thresh_x; | |||||
| value = mbut_state->value; | |||||
| if (incr) { | |||||
| if (!thresh_mx || mx <= thresh_mx) { | |||||
| value += val_step; | |||||
| thresh_mx = 0; | |||||
| } | |||||
| if (value > softmax) { | |||||
| value = softmax; | |||||
| if (!thresh_mx) | |||||
| thresh_mx = mx; | |||||
| } | |||||
| } | |||||
| else { | |||||
| if (!thresh_mx || mx <= thresh_mx) { | |||||
| value -= val_step; | |||||
| thresh_mx = 0; | |||||
| } | |||||
| if (value < softmin) | |||||
| value = softmin; | |||||
| if (!thresh_mx) | |||||
| thresh_mx = mx; | |||||
| } | |||||
| mbut_state->drag_thresh_x = thresh_mx; | |||||
| } | |||||
| else if (value > softmax) | |||||
| value = softmax; | |||||
| else if (value < softmin) | |||||
| value = softmin; | |||||
| if (value != mbut_state->value) { | |||||
| mbut_state->value = value; | |||||
| ui_set_but_val(but, value); | |||||
| ui_apply_but_func(C, but); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| #endif | |||||
| ED_region_tag_refresh_ui(data->ar); | |||||
| } | |||||
| } | |||||
| static int ui_step_active_find(ARegion *ar, uiVLadderData *data, const wmEvent *event) | |||||
| { | |||||
| rcti rect = ar->winrct, title_rect; | |||||
| const int step_y = UI_VLADDER_STEP_HEIGHT, mx = event->x, my = event->y; | |||||
| BLI_rcti_resize(&rect, BLI_rcti_size_x(&rect) - 2 * UI_ThemeMenuShadowWidth(), BLI_rcti_size_y(&rect)); | |||||
| title_rect = rect; | |||||
| title_rect.ymax = rect.ymax - 0.5f * UI_UNIT_Y; | |||||
| title_rect.ymin = title_rect.ymax - UI_UNIT_Y; | |||||
| rect.ymin += UI_ThemeMenuShadowWidth() + (data->totsteps - 1) * step_y; | |||||
| rect.ymax = rect.ymin + step_y; | |||||
| if (ui_mouse_inside_region(ar, mx, my)) { | |||||
| /* is the mouse inside the title/header? */ | |||||
| if (BLI_rcti_isect_pt(&title_rect, mx, my)) | |||||
| return -2; | |||||
| else { | |||||
| int i; | |||||
| for (i = 0; i < data->totsteps; i++, rect.ymin -= step_y, rect.ymax -= step_y) | |||||
| if (BLI_rcti_isect_pt(&rect, mx, my)) | |||||
| return i; | |||||
| } | |||||
| } | |||||
| return -1; | |||||
| } | |||||
| int ui_vladder_handle(bContext *C, const wmEvent *event, void *vldata) | |||||
| { | |||||
| uiVLadderData *data = vldata; | |||||
| uiPopupBlockHandle *puphandle = data->block->handle; | |||||
| ARegion *ar = data->ar; | |||||
| uiBut *but = data->but; | |||||
| uiHandleButtonData *hbdata = but->active; | |||||
| int mx = event->x, my = event->y; | |||||
| short retval = WM_UI_HANDLER_CONTINUE; | |||||
| const bool click = abs(mx - event->prevclickx) <= 3 && abs(my - event->prevclicky) <= 3; /* a mouse movement of 3px is still interpreted as a click */ | |||||
| switch (event->type) { | |||||
| case MOUSEMOVE: | |||||
| if (data->drag) { | |||||
| if (click && ui_is_a_warp_but(but)) | |||||
| WM_cursor_grab_enable(CTX_wm_window(C), true, true, NULL); | |||||
| ui_vladder_handle_numedit(C, event, data); | |||||
| } | |||||
| #ifdef USE_DRAG_POPUP | |||||
| else if (puphandle->is_grab) { | |||||
| int mdiff[2]; | |||||
| sub_v2_v2v2_int(mdiff, &event->x, puphandle->grab_xy_prev); | |||||
| copy_v2_v2_int(puphandle->grab_xy_prev, &event->x); | |||||
| add_v2_v2v2_int(puphandle->popup_create_vars.event_xy, puphandle->popup_create_vars.event_xy, mdiff); | |||||
| ui_popup_translate(C, ar, mdiff); | |||||
| } | |||||
| #endif | |||||
| else { | |||||
| data->step_active = ui_step_active_find(ar, data, event); | |||||
| ED_region_tag_refresh_ui(ar); /* ED_region_tag_redraw didn't work, this did */ | |||||
| } | |||||
| retval = WM_UI_HANDLER_CONTINUE; | |||||
| break; | |||||
| case PADENTER: | |||||
| case RETKEY: | |||||
| case LEFTMOUSE: | |||||
| if (event->val == KM_PRESS) { | |||||
| if (!click && data->step_active > -1) | |||||
| data->drag = true; | |||||
| #ifdef USE_DRAG_POPUP | |||||
| else if (!click && data->step_active == -2) { /* -2 == mouse inside header */ | |||||
| puphandle->is_grab = true; | |||||
| copy_v2_v2_int(puphandle->grab_xy_prev, &event->x); | |||||
| } | |||||
| #endif | |||||
| } | |||||
| else if (event->val == KM_RELEASE) { | |||||
| if (click && !data->drag) | |||||
| ui_vladder_remove(C, data); | |||||
| #ifdef USE_DRAG_POPUP | |||||
| else if (puphandle->is_grab) | |||||
| puphandle->is_grab = false; | |||||
| #endif | |||||
| WM_cursor_grab_disable(hbdata->window, NULL); | |||||
| data->drag = puphandle->is_grab = false; | |||||
| } | |||||
| retval = WM_UI_HANDLER_BREAK; | |||||
| break; | |||||
| case RIGHTMOUSE: | |||||
| case ESCKEY: | |||||
| hbdata->cancel = true; | |||||
| hbdata->escapecancel = true; | |||||
| #ifdef USE_DRAG_MULTINUM | |||||
| if (hbdata->multi_data.has_mbuts) | |||||
| ui_multibut_restore(hbdata, but->block); | |||||
| #endif | |||||
| ui_apply_but_func(C, but); | |||||
| ui_vladder_remove(C, data); | |||||
| retval = WM_UI_HANDLER_BREAK; | |||||
| break; | |||||
| } | |||||
| return retval; | |||||
| } | |||||
| /* *************** UI event handlers **************** */ | /* *************** UI event handlers **************** */ | ||||
| static int ui_handler_region(bContext *C, const wmEvent *event, void *UNUSED(userdata)) | static int ui_handler_region(bContext *C, const wmEvent *event, void *UNUSED(userdata)) | ||||
| Context not available. | |||||