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.
This commit is contained in:
Campbell Barton 2014-08-03 20:30:12 +10:00
parent e7b396c954
commit 47e7ce696c
3 changed files with 57 additions and 6 deletions

View File

@ -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 (ui_but_find_mouse_over(ar, event) != but) {
else if (but_other && ui_but_is_editable(but_other) && (but_other != but)) {
exit = true;
}
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);

View File

@ -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);

View File

@ -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;