Bugfix #28734
Double clicks were never working reliably in Blender - this mostly because it wasn't a real event, but something generated in the handler code. Now it is an actual event - meaning it always gets handled (if you have a keymap item for it of course), but if there's no doubleclick handling it treats the doubleclick as a normal click. Also cleaned code. No recursion anymore.
This commit is contained in:
@@ -1672,7 +1672,7 @@ static int wm_action_not_handled(int action)
|
||||
return action == WM_HANDLER_CONTINUE || action == (WM_HANDLER_BREAK | WM_HANDLER_MODAL);
|
||||
}
|
||||
|
||||
static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
|
||||
static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
const int do_debug_handler = (G.debug & G_DEBUG_EVENTS)
|
||||
@@ -1849,49 +1849,44 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
|
||||
}
|
||||
}
|
||||
|
||||
/* test for CLICK event */
|
||||
if (wm_action_not_handled(action) && event->val == KM_RELEASE) {
|
||||
if (action == (WM_HANDLER_BREAK | WM_HANDLER_MODAL))
|
||||
wm_cursor_arrow_move(CTX_wm_window(C), event);
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
/* this calls handlers twice - to solve (double-)click events */
|
||||
static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
|
||||
{
|
||||
int action = wm_handlers_do_intern(C, event, handlers);
|
||||
|
||||
/* test for CLICK events */
|
||||
if (wm_action_not_handled(action)) {
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
|
||||
if (win && win->eventstate->prevtype == event->type && win->eventstate->prevval == KM_PRESS) {
|
||||
/* test for double click first,
|
||||
* note1: this can be problematic because single click operators can get the
|
||||
* double click event but then with old mouse coords which is highly confusing,
|
||||
* so check for mouse moves too.
|
||||
* note2: the first click event will be handled but still used to create a
|
||||
* double click event if clicking again quickly.
|
||||
* If no double click events are found it will fallback to a single click.
|
||||
* So a double click event can result in 2 successive single click calls
|
||||
* if its not handled by the keymap - campbell */
|
||||
if ((ABS(event->x - win->eventstate->prevclickx)) <= 2 &&
|
||||
(ABS(event->y - win->eventstate->prevclicky)) <= 2 &&
|
||||
((PIL_check_seconds_timer() - win->eventstate->prevclicktime) * 1000 < U.dbl_click_time))
|
||||
{
|
||||
event->val = KM_DBL_CLICK;
|
||||
/* removed this because in cases where we're this is used as a single click
|
||||
* event, this will give old coords,
|
||||
* since the distance is checked above, using new coords should be ok. */
|
||||
// event->x = win->eventstate->prevclickx;
|
||||
// event->y = win->eventstate->prevclicky;
|
||||
action |= wm_handlers_do(C, event, handlers);
|
||||
}
|
||||
|
||||
if (wm_action_not_handled(action)) {
|
||||
|
||||
if (win && win->eventstate->prevtype == event->type) {
|
||||
|
||||
if(event->val == KM_RELEASE && win->eventstate->prevval == KM_PRESS) {
|
||||
event->val = KM_CLICK;
|
||||
action |= wm_handlers_do(C, event, handlers);
|
||||
action |= wm_handlers_do_intern(C, event, handlers);
|
||||
|
||||
/* revert value if not handled */
|
||||
if (wm_action_not_handled(action)) {
|
||||
event->val = KM_RELEASE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* revert value if not handled */
|
||||
if (wm_action_not_handled(action)) {
|
||||
event->val = KM_RELEASE;
|
||||
else if(event->val == KM_DBL_CLICK) {
|
||||
event->val = KM_PRESS;
|
||||
action |= wm_handlers_do_intern(C, event, handlers);
|
||||
|
||||
/* revert value if not handled */
|
||||
if (wm_action_not_handled(action)) {
|
||||
event->val = KM_DBL_CLICK;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (action == (WM_HANDLER_BREAK | WM_HANDLER_MODAL))
|
||||
wm_cursor_arrow_move(CTX_wm_window(C), event);
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
@@ -2185,40 +2180,6 @@ void wm_event_do_handlers(bContext *C)
|
||||
}
|
||||
}
|
||||
|
||||
/* store last event for this window */
|
||||
/* mousemove and timer events don't overwrite last type */
|
||||
if (!ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) && !ISTIMER(event->type)) {
|
||||
if (wm_action_not_handled(action)) {
|
||||
if (win->eventstate->prevtype == event->type) {
|
||||
/* set click time on first click (press -> release) */
|
||||
if (win->eventstate->prevval == KM_PRESS && event->val == KM_RELEASE) {
|
||||
win->eventstate->prevclicktime = PIL_check_seconds_timer();
|
||||
win->eventstate->prevclickx = event->x;
|
||||
win->eventstate->prevclicky = event->y;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* reset click time if event type not the same */
|
||||
win->eventstate->prevclicktime = 0;
|
||||
}
|
||||
|
||||
win->eventstate->prevval = event->val;
|
||||
win->eventstate->prevtype = event->type;
|
||||
}
|
||||
else if (event->val == KM_CLICK) { /* keep click for double click later */
|
||||
win->eventstate->prevtype = event->type;
|
||||
win->eventstate->prevval = event->val;
|
||||
win->eventstate->prevclicktime = PIL_check_seconds_timer();
|
||||
win->eventstate->prevclickx = event->x;
|
||||
win->eventstate->prevclicky = event->y;
|
||||
}
|
||||
else { /* reset if not */
|
||||
win->eventstate->prevtype = -1;
|
||||
win->eventstate->prevval = 0;
|
||||
win->eventstate->prevclicktime = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* unlink and free here, blender-quit then frees all */
|
||||
BLI_remlink(&win->queue, event);
|
||||
wm_event_free(event);
|
||||
@@ -2724,6 +2685,10 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
|
||||
/* initialize and copy state (only mouse x y and modifiers) */
|
||||
event = *evt;
|
||||
|
||||
/* copy prev state to event state */
|
||||
evt->prevval = evt->val;
|
||||
evt->prevtype = evt->type;
|
||||
|
||||
switch (type) {
|
||||
/* mouse move */
|
||||
case GHOST_kEventCursorMove:
|
||||
@@ -2806,7 +2771,8 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
|
||||
case GHOST_kEventButtonUp:
|
||||
{
|
||||
GHOST_TEventButtonData *bd = customdata;
|
||||
|
||||
|
||||
/* get value and type from ghost */
|
||||
event.val = (type == GHOST_kEventButtonDown) ? KM_PRESS : KM_RELEASE;
|
||||
|
||||
if (bd->button == GHOST_kButtonMaskLeft)
|
||||
@@ -2820,6 +2786,10 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
|
||||
else
|
||||
event.type = MIDDLEMOUSE;
|
||||
|
||||
/* copy to event state */
|
||||
evt->val= event.val;
|
||||
evt->type= event.type;
|
||||
|
||||
if (win->active == 0) {
|
||||
int cx, cy;
|
||||
|
||||
@@ -2830,6 +2800,21 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
|
||||
event.y = evt->y = cy;
|
||||
}
|
||||
|
||||
/* double click test */
|
||||
if (event.type == evt->prevtype && event.val == KM_PRESS) {
|
||||
if ((ABS(event.x - evt->prevclickx)) <= 2 &&
|
||||
(ABS(event.y - evt->prevclicky)) <= 2 &&
|
||||
((PIL_check_seconds_timer() - evt->prevclicktime) * 1000 < U.dbl_click_time))
|
||||
{
|
||||
event.val = KM_DBL_CLICK;
|
||||
}
|
||||
}
|
||||
if (event.val == KM_RELEASE) {
|
||||
evt->prevclicktime = PIL_check_seconds_timer();
|
||||
evt->prevclickx = event.x;
|
||||
evt->prevclicky = event.y;
|
||||
}
|
||||
|
||||
/* add to other window if event is there (not to both!) */
|
||||
owin = wm_event_cursor_other_windows(wm, win, &event);
|
||||
if (owin) {
|
||||
@@ -2860,6 +2845,10 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
|
||||
memcpy(event.utf8_buf, kd->utf8_buf, sizeof(event.utf8_buf)); /* might be not null terminated*/
|
||||
event.val = (type == GHOST_kEventKeyDown) ? KM_PRESS : KM_RELEASE;
|
||||
|
||||
/* copy to event state */
|
||||
evt->val= event.val;
|
||||
evt->type= event.type;
|
||||
|
||||
/* exclude arrow keys, esc, etc from text input */
|
||||
if (type == GHOST_kEventKeyUp) {
|
||||
event.ascii = '\0';
|
||||
@@ -2932,6 +2921,22 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
|
||||
if (event.type == ESCKEY && event.val == KM_PRESS)
|
||||
G.is_break = TRUE;
|
||||
|
||||
/* double click test */
|
||||
if (event.type == evt->prevtype && event.val == KM_PRESS) {
|
||||
if ((ABS(event.x - evt->prevclickx)) <= 2 &&
|
||||
(ABS(event.y - evt->prevclicky)) <= 2 &&
|
||||
((PIL_check_seconds_timer() - evt->prevclicktime) * 1000 < U.dbl_click_time))
|
||||
{
|
||||
printf("double key click\n");
|
||||
event.val = KM_DBL_CLICK;
|
||||
}
|
||||
}
|
||||
if (event.val == KM_RELEASE) {
|
||||
evt->prevclicktime = PIL_check_seconds_timer();
|
||||
evt->prevclickx = event.x;
|
||||
evt->prevclicky = event.y;
|
||||
}
|
||||
|
||||
wm_event_add(win, &event);
|
||||
|
||||
break;
|
||||
|
||||
@@ -957,11 +957,6 @@ int WM_keymap_item_compare(wmKeyMapItem *k1, wmKeyMapItem *k2)
|
||||
return 0;
|
||||
|
||||
if (k1->val != KM_ANY && k2->val != KM_ANY) {
|
||||
/* take click, press, release conflict into account */
|
||||
if (k1->val == KM_CLICK && ELEM3(k2->val, KM_PRESS, KM_RELEASE, KM_CLICK) == 0)
|
||||
return 0;
|
||||
if (k2->val == KM_CLICK && ELEM3(k1->val, KM_PRESS, KM_RELEASE, KM_CLICK) == 0)
|
||||
return 0;
|
||||
if (k1->val != k2->val)
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user