From 47e7ce696c997313ae30bca0c09d17d6886a155b Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sun, 3 Aug 2014 20:30:12 +1000 Subject: Fix for glitch with menus not reliably setting an active item (D674) When menus are clamped to the window bounds, its was possible not to have an active menu-item under the mouse, Making Ctrl+S,Enter not completely reliable. Changes needed to support this are: - menu item is activated on popup menus (to avoid relying on mouse-over) - moving mouse away from menu items only de-activates when over a new menu-item. - Mouse clicks are ignored if they are not directly over the menu item. --- .../blender/editors/interface/interface_handlers.c | 42 ++++++++++++++++++---- .../blender/editors/interface/interface_intern.h | 2 ++ .../blender/editors/interface/interface_regions.c | 19 ++++++++++ 3 files changed, 57 insertions(+), 6 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index b3f13ccdb35..b5f11c2a0f9 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -385,7 +385,7 @@ void ui_pan_to_scroll(const wmEvent *event, int *type, int *val) } } -static bool ui_but_is_editable(const uiBut *but) +bool ui_but_is_editable(const uiBut *but) { return !ELEM(but->type, LABEL, SEPR, SEPRLINE, ROUNDBOX, LISTBOX, PROGRESSBAR); } @@ -7199,6 +7199,17 @@ void ui_button_activate_do(bContext *C, ARegion *ar, uiBut *but) ui_do_button(C, but->block, but, &event); } +/** + * Simulate moving the mouse over a button (or navigating to it with arrow keys). + * + * exported so menus can start with a highlighted button, + * even if the mouse isnt over it + */ +void ui_button_activate_over(bContext *C, ARegion *ar, uiBut *but) +{ + button_activate_init(C, ar, but, BUTTON_ACTIVATE_OVER); +} + void ui_button_execute_begin(struct bContext *UNUSED(C), struct ARegion *ar, uiBut *but, void **active_back) { /* note: ideally we would not have to change 'but->active' however @@ -7261,12 +7272,20 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) retval = WM_UI_HANDLER_CONTINUE; break; case MOUSEMOVE: - /* verify if we are still over the button, if not exit */ - if (!ui_mouse_inside_button(ar, but, event->x, event->y)) { - data->cancel = true; - button_activate_state(C, but, BUTTON_STATE_EXIT); + { + uiBut *but_other = ui_but_find_mouse_over(ar, event); + bool exit = false; + + if (!ui_block_is_menu(block) && + !ui_mouse_inside_button(ar, but, event->x, event->y)) + { + exit = true; + } + else if (but_other && ui_but_is_editable(but_other) && (but_other != but)) { + exit = true; } - else if (ui_but_find_mouse_over(ar, event) != but) { + + if (exit) { data->cancel = true; button_activate_state(C, but, BUTTON_STATE_EXIT); } @@ -7277,6 +7296,7 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) } break; + } case TIMER: { /* handle tooltip timer */ @@ -7859,6 +7879,16 @@ static int ui_handle_menu_button(bContext *C, const wmEvent *event, uiPopupBlock uiBut *but = ui_but_find_activated(ar); int retval; + if (but) { + /* Its possible there is an active menu item NOT under the mouse, + * in this case ignore mouse clicks outside the button (but Enter etc is accepted) */ + if ((event->type != MOUSEMOVE) && ISMOUSE(event->type)) { + if (!ui_mouse_inside_button(but->active->region, but, event->x, event->y)) { + but = NULL; + } + } + } + if (but) { ScrArea *ctx_area = CTX_wm_area(C); ARegion *ctx_region = CTX_wm_region(C); diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index fa8b6b1ffee..7d03aaea6b3 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -552,6 +552,7 @@ void ui_draw_but_NODESOCKET(ARegion *ar, uiBut *but, struct uiWidgetColors *wcol PointerRNA *ui_handle_afterfunc_add_operator(struct wmOperatorType *ot, int opcontext, bool create_props); extern void ui_pan_to_scroll(const struct wmEvent *event, int *type, int *val); extern void ui_button_activate_do(struct bContext *C, struct ARegion *ar, uiBut *but); +extern void ui_button_activate_over(struct bContext *C, struct ARegion *ar, uiBut *but); extern void ui_button_execute_begin(struct bContext *C, struct ARegion *ar, uiBut *but, void **active_back); extern void ui_button_execute_end(struct bContext *C, struct ARegion *ar, uiBut *but, void *active_back); extern void ui_button_active_free(const struct bContext *C, uiBut *but); @@ -559,6 +560,7 @@ extern bool ui_button_is_active(struct ARegion *ar) ATTR_WARN_UNUSED_RESULT; extern int ui_button_open_menu_direction(uiBut *but); extern void ui_button_text_password_hide(char password_str[UI_MAX_DRAW_STR], uiBut *but, const bool restore); extern uiBut *ui_but_find_activated(struct ARegion *ar); +bool ui_but_is_editable(const uiBut *but); void ui_button_clipboard_free(void); void ui_panel_menu(struct bContext *C, ARegion *ar, Panel *pa); diff --git a/source/blender/editors/interface/interface_regions.c b/source/blender/editors/interface/interface_regions.c index 85bf15e15cf..32fb684f2b6 100644 --- a/source/blender/editors/interface/interface_regions.c +++ b/source/blender/editors/interface/interface_regions.c @@ -2408,6 +2408,7 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi uiBlockSetFlag(block, UI_BLOCK_MOVEMOUSE_QUIT); if (pup->popup) { + uiBut *but_activate = NULL; uiBlockSetFlag(block, UI_BLOCK_LOOP | UI_BLOCK_REDRAW | UI_BLOCK_NUMSELECT); uiBlockSetDirection(block, direction); @@ -2421,6 +2422,10 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi * block to be under the mouse */ offset[0] = -(bt->rect.xmin + 0.8f * BLI_rctf_size_x(&bt->rect)); offset[1] = -(bt->rect.ymin + 0.5f * UI_UNIT_Y); + + if (ui_but_is_editable(bt)) { + but_activate = bt; + } } else { /* position mouse at 0.8*width of the button and below the tile @@ -2430,6 +2435,20 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi offset[0] = min_ii(offset[0], -(bt->rect.xmin + 0.8f * BLI_rctf_size_x(&bt->rect))); offset[1] = 2.1 * UI_UNIT_Y; + + for (bt = block->buttons.first; bt; bt = bt->next) { + if (ui_but_is_editable(bt)) { + but_activate = bt; + break; + } + } + } + + /* in rare cases this is needed since moving the popup + * to be within the window bounds may move it away from the mouse, + * This ensures we set an item to be active. */ + if (but_activate) { + ui_button_activate_over(C, handle->region, but_activate); } block->minbounds = minwidth; -- cgit v1.2.3