Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/interface/interface_handlers.c')
-rw-r--r--source/blender/editors/interface/interface_handlers.c190
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) {