diff options
Diffstat (limited to 'source/blender/editors/interface/interface_handlers.c')
-rw-r--r-- | source/blender/editors/interface/interface_handlers.c | 190 |
1 files changed, 126 insertions, 64 deletions
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 9a7e189406c..ebde1d54c07 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -381,6 +381,9 @@ typedef struct uiHandleButtonData { uiSelectContextStore select_others; #endif + /* Text field undo. */ + struct uiUndoStack_Text *undo_stack_text; + /* post activate */ uiButtonActivateType posttype; uiBut *postbut; @@ -417,7 +420,7 @@ typedef struct uiAfterFunc { PropertyRNA *rnaprop; void *search_arg; - uiButSearchArgFreeFunc search_arg_free_func; + uiButSearchArgFreeFn search_arg_free_fn; bContextStore *context; @@ -753,10 +756,12 @@ static void ui_apply_but_func(bContext *C, uiBut *but) after->rnapoin = but->rnapoin; after->rnaprop = but->rnaprop; - after->search_arg_free_func = but->search_arg_free_func; - after->search_arg = but->search_arg; - but->search_arg_free_func = NULL; - but->search_arg = NULL; + if (but->search != NULL) { + after->search_arg_free_fn = but->search->arg_free_fn; + after->search_arg = but->search->arg; + but->search->arg_free_fn = NULL; + but->search->arg = NULL; + } if (but->context) { after->context = CTX_store_copy(but->context); @@ -924,8 +929,8 @@ static void ui_apply_but_funcs_after(bContext *C) MEM_freeN(after.rename_orig); } - if (after.search_arg_free_func) { - after.search_arg_free_func(after.search_arg); + if (after.search_arg_free_fn) { + after.search_arg_free_fn(after.search_arg); } ui_afterfunc_update_preferences_dirty(&after); @@ -2848,6 +2853,23 @@ static bool ui_textedit_delete_selection(uiBut *but, uiHandleButtonData *data) return changed; } +static bool ui_textedit_set_cursor_pos_foreach_glyph(const char *UNUSED(str), + const size_t str_step_ofs, + const rcti *glyph_step_bounds, + const int UNUSED(glyph_advance_x), + const rctf *glyph_bounds, + const int UNUSED(glyph_bearing[2]), + void *user_data) +{ + int *cursor_data = user_data; + float center = glyph_step_bounds->xmin + (BLI_rctf_size_x(glyph_bounds) / 2.0f); + if (cursor_data[0] < center) { + cursor_data[1] = str_step_ofs; + return false; + } + return true; +} + /** * \param x: Screen space cursor location - #wmEvent.x * @@ -2883,8 +2905,7 @@ static void ui_textedit_set_cursor_pos(uiBut *but, uiHandleButtonData *data, con startx += UI_DPI_ICON_SIZE / aspect; } } - /* But this extra .05 makes clicks in between characters feel nicer. */ - startx += ((UI_TEXT_MARGIN_X + 0.05f) * U.widget_unit) / aspect; + startx += (UI_TEXT_MARGIN_X * U.widget_unit) / aspect; /* mouse dragged outside the widget to the left */ if (x < startx) { @@ -2907,48 +2928,24 @@ static void ui_textedit_set_cursor_pos(uiBut *but, uiHandleButtonData *data, con but->pos = but->ofs; } /* mouse inside the widget, mouse coords mapped in widget space */ - else { /* (x >= startx) */ - int pos_i; - - /* keep track of previous distance from the cursor to the char */ - float cdist, cdist_prev = 0.0f; - short pos_prev; - - str_last = &str[strlen(str)]; - - but->pos = pos_prev = ((str_last - str) - but->ofs); - - while (true) { - cdist = startx + BLF_width(fstyle.uifont_id, str + but->ofs, (str_last - str) - but->ofs); - - /* check if position is found */ - if (cdist < x) { - /* check is previous location was in fact closer */ - if ((x - cdist) > (cdist_prev - x)) { - but->pos = pos_prev; - } - break; - } - cdist_prev = cdist; - pos_prev = but->pos; - /* done with tricky distance checks */ - - pos_i = but->pos; - if (but->pos <= 0) { - break; - } - if (BLI_str_cursor_step_prev_utf8(str + but->ofs, but->ofs, &pos_i)) { - but->pos = pos_i; - str_last = &str[but->pos + but->ofs]; - } - else { - break; /* unlikely but possible */ - } - } - but->pos += but->ofs; - if (but->pos < 0) { - but->pos = 0; - } + else { + str_last = &str[but->ofs]; + const int str_last_len = strlen(str_last); + int x_pos = (int)(x - startx); + int glyph_data[2] = { + x_pos, /* horizontal position to test. */ + -1, /* Write the character offset here. */ + }; + BLF_boundbox_foreach_glyph(fstyle.uifont_id, + str + but->ofs, + INT_MAX, + ui_textedit_set_cursor_pos_foreach_glyph, + glyph_data); + /* If value untouched then we are to the right. */ + if (glyph_data[1] == -1) { + glyph_data[1] = str_last_len; + } + but->pos = glyph_data[1] + but->ofs; } if (fstyle.kerning == 1) { @@ -3314,9 +3311,13 @@ static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data) but->selsta = 0; but->selend = len; + /* Initialize undo history tracking. */ + data->undo_stack_text = ui_textedit_undo_stack_create(); + ui_textedit_undo_push(data->undo_stack_text, but->editstr, but->pos); + /* optional searchbox */ if (but->type == UI_BTYPE_SEARCH_MENU) { - data->searchbox = but->search_create_func(C, data->region, but); + data->searchbox = but->search->create_fn(C, data->region, but); ui_searchbox_update(C, data->searchbox, but, true); /* true = reset */ } @@ -3356,6 +3357,9 @@ static void ui_textedit_end(bContext *C, uiBut *but, uiHandleButtonData *data) /* ensure menu (popup) too is closed! */ data->escapecancel = true; + + WM_reportf(RPT_ERROR, "Failed to find '%s'", but->editstr); + WM_report_banner_show(); } } @@ -3369,6 +3373,10 @@ static void ui_textedit_end(bContext *C, uiBut *but, uiHandleButtonData *data) WM_cursor_modal_restore(win); + /* Free text undo history text blocks. */ + ui_textedit_undo_stack_destroy(data->undo_stack_text); + data->undo_stack_text = NULL; + #ifdef WITH_INPUT_IME if (win->ime_data) { ui_textedit_ime_end(win, but); @@ -3448,7 +3456,7 @@ static void ui_do_but_textedit( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { int retval = WM_UI_HANDLER_CONTINUE; - bool changed = false, inbox = false, update = false; + bool changed = false, inbox = false, update = false, skip_undo_push = false; #ifdef WITH_INPUT_IME wmWindow *win = CTX_wm_window(C); @@ -3468,7 +3476,7 @@ static void ui_do_but_textedit( /* pass */ } else { - ui_searchbox_event(C, data->searchbox, but, event); + ui_searchbox_event(C, data->searchbox, but, data->region, event); } #else ui_searchbox_event(C, data->searchbox, but, event); @@ -3479,6 +3487,16 @@ static void ui_do_but_textedit( case RIGHTMOUSE: case EVT_ESCKEY: if (event->val == KM_PRESS) { + /* Support search context menu. */ + if (event->type == RIGHTMOUSE) { + if (data->searchbox) { + if (ui_searchbox_event(C, data->searchbox, but, data->region, event)) { + /* Only break if the event was handled. */ + break; + } + } + } + #ifdef WITH_INPUT_IME /* skips button handling since it is not wanted */ if (is_ime_composing) { @@ -3587,7 +3605,7 @@ static void ui_do_but_textedit( #ifdef USE_KEYNAV_LIMIT ui_mouse_motion_keynav_init(&data->searchbox_keynav_state, event); #endif - ui_searchbox_event(C, data->searchbox, but, event); + ui_searchbox_event(C, data->searchbox, but, data->region, event); break; } if (event->type == WHEELDOWNMOUSE) { @@ -3604,7 +3622,7 @@ static void ui_do_but_textedit( #ifdef USE_KEYNAV_LIMIT ui_mouse_motion_keynav_init(&data->searchbox_keynav_state, event); #endif - ui_searchbox_event(C, data->searchbox, but, event); + ui_searchbox_event(C, data->searchbox, but, data->region, event); break; } if (event->type == WHEELUPMOUSE) { @@ -3670,6 +3688,32 @@ static void ui_do_but_textedit( } retval = WM_UI_HANDLER_BREAK; break; + case EVT_ZKEY: { + /* Ctrl-Z or Ctrl-Shift-Z: Undo/Redo (allowing for OS-Key on Apple). */ + + const bool is_redo = (event->shift != 0); + if ( +#if defined(__APPLE__) + (event->oskey && !IS_EVENT_MOD(event, alt, ctrl)) || +#endif + (event->ctrl && !IS_EVENT_MOD(event, alt, oskey))) { + int undo_pos; + const char *undo_str = ui_textedit_undo( + data->undo_stack_text, is_redo ? 1 : -1, &undo_pos); + if (undo_str != NULL) { + ui_textedit_string_set(but, data, undo_str); + + /* Set the cursor & clear selection. */ + but->pos = undo_pos; + but->selsta = but->pos; + but->selend = but->pos; + changed = true; + } + retval = WM_UI_HANDLER_BREAK; + skip_undo_push = true; + } + break; + } } if ((event->ascii || event->utf8_buf[0]) && (retval == WM_UI_HANDLER_CONTINUE) @@ -3723,6 +3767,11 @@ static void ui_do_but_textedit( #endif if (changed) { + /* The undo stack may be NULL if an event exits editing. */ + if ((skip_undo_push == false) && (data->undo_stack_text != NULL)) { + ui_textedit_undo_push(data->undo_stack_text, data->str, but->pos); + } + /* only do live update when but flag request it (UI_BUT_TEXTEDIT_UPDATE). */ if (update && data->interactive) { ui_apply_but(C, block, but, data, true); @@ -4397,7 +4446,8 @@ static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, cons do_activate = (event->val == KM_RELEASE); } else { - do_activate = (event->val == KM_PRESS); + /* Also use double-clicks to prevent fast clicks to leak to other handlers (T76481). */ + do_activate = ELEM(event->val, KM_PRESS, KM_DBL_CLICK); } } @@ -8213,11 +8263,22 @@ static uiBut *ui_context_rna_button_active(const bContext *C) return ui_context_button_active(CTX_wm_region(C), ui_context_rna_button_active_test); } -uiBut *UI_context_active_but_get(const struct bContext *C) +uiBut *UI_context_active_but_get(const bContext *C) { return ui_context_button_active(CTX_wm_region(C), NULL); } +/* + * Version of #UI_context_active_get() that uses the result of #CTX_wm_menu() + * if set. Does not traverse into parent menus, which may be wanted in some + * cases. + */ +uiBut *UI_context_active_but_get_respect_menu(const bContext *C) +{ + ARegion *ar_menu = CTX_wm_menu(C); + return ui_context_button_active(ar_menu ? ar_menu : CTX_wm_region(C), NULL); +} + uiBut *UI_region_active_but_get(ARegion *region) { return ui_context_button_active(region, NULL); @@ -8814,7 +8875,7 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) * This is needed to make sure if a button was active, * it stays active while the mouse is over it. * This avoids adding mousemoves, see: [#33466] */ - if (ELEM(state_orig, BUTTON_STATE_INIT, BUTTON_STATE_HIGHLIGHT)) { + if (ELEM(state_orig, BUTTON_STATE_INIT, BUTTON_STATE_HIGHLIGHT, BUTTON_STATE_WAIT_DRAG)) { if (ui_but_find_mouse_over(region, event) == but) { button_activate_init(C, region, but, BUTTON_ACTIVATE_OVER); } @@ -9339,6 +9400,11 @@ static int ui_handle_menu_button(bContext *C, const wmEvent *event, uiPopupBlock if (event->val == KM_RELEASE) { /* pass, needed so we can exit active menu-items when click-dragging out of them */ } + else if (but->type == UI_BTYPE_SEARCH_MENU) { + /* Pass, needed so search popup can have RMB context menu. + * This may be useful for other interactions which happen in the search popup + * without being directly over the search button. */ + } else if (!ui_block_is_menu(but->block) || ui_block_is_pie_menu(but->block)) { /* pass, skip for dialogs */ } @@ -10772,9 +10838,6 @@ static int ui_popup_handler(bContext *C, const wmEvent *event, void *userdata) if (temp.popup_func) { temp.popup_func(C, temp.popup_arg, temp.retvalue); } - if (temp.optype) { - WM_operator_name_call_ptr(C, temp.optype, temp.opcontext, NULL); - } } else if (temp.cancel_func) { temp.cancel_func(C, temp.popup_arg); @@ -10938,8 +11001,7 @@ void UI_screen_free_active_but(const bContext *C, bScreen *screen) { wmWindow *win = CTX_wm_window(C); - ED_screen_areas_iter(win, screen, area) - { + ED_screen_areas_iter (win, screen, area) { LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { uiBut *but = ui_region_find_active_but(region); if (but) { |