[Bf-blender-cvs] [1f1dcf41d51] master: Event System: key-press while dragging now activates drag first

Campbell Barton noreply at git.blender.org
Thu Mar 10 11:28:39 CET 2022


Commit: 1f1dcf41d51a03150ee38f220c590f8715b11e88
Author: Campbell Barton
Date:   Thu Mar 10 16:23:24 2022 +1100
Branches: master
https://developer.blender.org/rB1f1dcf41d51a03150ee38f220c590f8715b11e88

Event System: key-press while dragging now activates drag first

When dragging with a large threshold (using a tablet for example),
it's possible to press another key before the drag threshold is reached.

So tweaking then pressing X would show the delete popup instead of
transforming along the X-axis.

Now key presses while dragging cause the drag event to be evaluated
before the key press.

Note that to properly base the mouse-move event on the previous
state the last handled event is now stored in the window.
Without this the inserted mouse-move event may contain invalid values
from the next event (it's modifier state or other `prev_*` values).

Requested by @JulienKaspar.

===================================================================

M	source/blender/editors/interface/interface_region_popup.c
M	source/blender/makesdna/DNA_windowmanager_types.h
M	source/blender/windowmanager/WM_types.h
M	source/blender/windowmanager/intern/wm.c
M	source/blender/windowmanager/intern/wm_event_system.c
M	source/blender/windowmanager/intern/wm_files.c
M	source/blender/windowmanager/intern/wm_window.c

===================================================================

diff --git a/source/blender/editors/interface/interface_region_popup.c b/source/blender/editors/interface/interface_region_popup.c
index c37d8ec7a2b..2f2556225b5 100644
--- a/source/blender/editors/interface/interface_region_popup.c
+++ b/source/blender/editors/interface/interface_region_popup.c
@@ -557,6 +557,7 @@ uiBlock *ui_popup_block_refresh(bContext *C,
 
 #ifdef DEBUG
   wmEvent *event_back = window->eventstate;
+  wmEvent *event_last_back = window->event_last_handled;
 #endif
 
   /* create ui block */
@@ -740,6 +741,7 @@ uiBlock *ui_popup_block_refresh(bContext *C,
 
 #ifdef DEBUG
   window->eventstate = event_back;
+  window->event_last_handled = event_last_back;
 #endif
 
   return block;
diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h
index 42c94832a43..ade0fcdb13f 100644
--- a/source/blender/makesdna/DNA_windowmanager_types.h
+++ b/source/blender/makesdna/DNA_windowmanager_types.h
@@ -294,6 +294,8 @@ typedef struct wmWindow {
 
   /** Storage for event system. */
   struct wmEvent *eventstate;
+  /** Keep the last handled event in `event_queue` here (owned and must be freed). */
+  struct wmEvent *event_last_handled;
 
   /* Input Method Editor data - complex character input (especially for Asian character input)
    * Currently WIN32 and APPLE, runtime-only data. */
diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h
index 8934f714c21..9edbafafdd3 100644
--- a/source/blender/windowmanager/WM_types.h
+++ b/source/blender/windowmanager/WM_types.h
@@ -610,6 +610,11 @@ typedef enum eWM_EventFlag {
    * See #KMI_REPEAT_IGNORE for details on how key-map handling uses this.
    */
   WM_EVENT_IS_REPEAT = (1 << 1),
+  /**
+   * Mouse-move events may have this flag set to force creating a click-drag event
+   * even when the threshold has not been met.
+   */
+  WM_EVENT_FORCE_DRAG_THRESHOLD = (1 << 2),
 } eWM_EventFlag;
 
 typedef struct wmTabletData {
diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c
index 487eafc736c..40d9b0b9a35 100644
--- a/source/blender/windowmanager/intern/wm.c
+++ b/source/blender/windowmanager/intern/wm.c
@@ -155,6 +155,7 @@ static void window_manager_blend_read_data(BlendDataReader *reader, ID *id)
     win->ghostwin = NULL;
     win->gpuctx = NULL;
     win->eventstate = NULL;
+    win->event_last_handled = NULL;
     win->cursor_keymap_status = NULL;
 #if defined(WIN32) || defined(__APPLE__)
     win->ime_data = NULL;
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index 47f1f36dceb..3fe8e6cd1b0 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -100,6 +100,7 @@ static int wm_operator_call_internal(bContext *C,
                                      wmEvent *event);
 
 static bool wm_operator_check_locked_interface(bContext *C, wmOperatorType *ot);
+static wmEvent *wm_event_add_mousemove_to_head(wmWindow *win);
 
 /* -------------------------------------------------------------------- */
 /** \name Event Management
@@ -200,6 +201,30 @@ void wm_event_free(wmEvent *event)
   MEM_freeN(event);
 }
 
+/** A version of #wm_event_free that holds the last handled event. */
+static void wm_event_free_last_handled(wmWindow *win, wmEvent *event)
+{
+  /* Don't rely on this pointer being valid,
+   * callers should behave as if the memory has been freed.
+   * As this function should be interchangeable with #wm_event_free. */
+#ifndef NDEBUG
+  {
+    wmEvent *event_copy = MEM_dupallocN(event);
+    MEM_freeN(event);
+    event = event_copy;
+  }
+#endif
+
+  if (win->event_last_handled) {
+    wm_event_free(win->event_last_handled);
+  }
+  /* Don't store custom data in the last handled event as we don't have control how long this event
+   * will be stored and the referenced data may become invalid (also it's not needed currently). */
+  wm_event_custom_free(event);
+  wm_event_custom_clear(event);
+  win->event_last_handled = event;
+}
+
 static void wm_event_free_last(wmWindow *win)
 {
   wmEvent *event = BLI_poptail(&win->event_queue);
@@ -3176,7 +3201,8 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
      * in `action` setting #WM_HANDLER_HANDLED, but not #WM_HANDLER_BREAK. */
     if ((action & WM_HANDLER_BREAK) == 0 || wm_action_not_handled(action)) {
       if (win->event_queue_check_drag) {
-        if (WM_event_drag_test(event, event->prev_press_xy)) {
+        if ((event->flag & WM_EVENT_FORCE_DRAG_THRESHOLD) ||
+            WM_event_drag_test(event, event->prev_press_xy)) {
           win->event_queue_check_drag_handled = true;
           const int direction = WM_event_drag_direction(event);
 
@@ -3660,6 +3686,17 @@ void wm_event_do_handlers(bContext *C)
     while ((event = win->event_queue.first)) {
       int action = WM_HANDLER_CONTINUE;
 
+      /* Force handling drag if a key is pressed even if the drag threshold has not been met.
+       * Needed so tablet actions (which typically use a larger threshold) can click-drag
+       * then press keys - activating the drag action early. */
+      if (win->event_queue_check_drag) {
+        if ((event->val == KM_PRESS) && ((event->flag & WM_EVENT_IS_REPEAT) == 0) &&
+            ISKEYBOARD_OR_BUTTON(event->type)) {
+          event = wm_event_add_mousemove_to_head(win);
+          event->flag |= WM_EVENT_FORCE_DRAG_THRESHOLD;
+        }
+      }
+
       /* Active screen might change during handlers, update pointer. */
       screen = WM_window_get_active_screen(win);
 
@@ -3675,7 +3712,7 @@ void wm_event_do_handlers(bContext *C)
           CLOG_INFO(WM_LOG_HANDLERS, 1, "event filtered due to pie button pressed");
         }
         BLI_remlink(&win->event_queue, event);
-        wm_event_free(event);
+        wm_event_free_last_handled(win, event);
         continue;
       }
 
@@ -3685,7 +3722,7 @@ void wm_event_do_handlers(bContext *C)
       if (event->type == EVT_XR_ACTION) {
         wm_event_handle_xrevent(C, wm, win, event);
         BLI_remlink(&win->event_queue, event);
-        wm_event_free(event);
+        wm_event_free_last_handled(win, event);
         /* Skip mouse event handling below, which is unnecessary for XR events. */
         continue;
       }
@@ -3823,7 +3860,7 @@ void wm_event_do_handlers(bContext *C)
 
       /* Un-link and free here, Blender-quit then frees all. */
       BLI_remlink(&win->event_queue, event);
-      wm_event_free(event);
+      wm_event_free_last_handled(win, event);
     }
 
     /* Only add mouse-move when the event queue was read entirely. */
@@ -4752,6 +4789,38 @@ static wmEvent *wm_event_add_mousemove(wmWindow *win, const wmEvent *event)
   return event_new;
 }
 
+static wmEvent *wm_event_add_mousemove_to_head(wmWindow *win)
+{
+  /* Use the last handled event instead of `win->eventstate` because the state of the modifiers
+   * and previous values should be set based on the last state, not using values from the future.
+   * So this gives an accurate simulation of mouse motion before the next event is handled. */
+  const wmEvent *event_last = win->event_last_handled;
+
+  wmEvent tevent;
+  if (event_last) {
+    tevent = *event_last;
+
+    tevent.flag = 0;
+    tevent.ascii = '\0';
+    tevent.utf8_buf[0] = '\0';
+
+    wm_event_custom_clear(&tevent);
+  }
+  else {
+    memset(&tevent, 0x0, sizeof(tevent));
+  }
+
+  tevent.type = MOUSEMOVE;
+  copy_v2_v2_int(tevent.prev_xy, tevent.xy);
+
+  wmEvent *event_new = wm_event_add(win, &tevent);
+  BLI_remlink(&win->event_queue, event_new);
+  BLI_addhead(&win->event_queue, event_new);
+
+  copy_v2_v2_int(event_new->prev_xy, event_last->xy);
+  return event_new;
+}
+
 static wmEvent *wm_event_add_trackpad(wmWindow *win, const wmEvent *event, int deltax, int deltay)
 {
   /* Ignore in between track-pad events for performance, we only need high accuracy
diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c
index 5bf5780b93f..4ff96b82fd2 100644
--- a/source/blender/windowmanager/intern/wm_files.c
+++ b/source/blender/windowmanager/intern/wm_files.c
@@ -246,7 +246,9 @@ static void wm_window_substitute_old(wmWindowManager *oldwm,
   oldwin->gpuctx = NULL;
 
   win->eventstate = oldwin->eventstate;
+  win->event_last_handled = oldwin->event_last_handled;
   oldwin->eventstate = NULL;
+  oldwin->event_last_handled = NULL;
 
   /* Ensure proper screen re-scaling. */
   win->sizex = oldwin->sizex;
diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c
index 6c90e0603a5..d6f85e3795e 100644
--- a/source/blender/windowmanager/intern/wm_window.c
+++ b/source/blender/windowmanager/intern/wm_window.c
@@ -209,6 +209,9 @@ void wm_window_free(bContext *C, wmWindowManager *wm, wmWindow *win)
   if (win->eventstate) {
     MEM_freeN(win->eventstate);
   }
+  if (win->event_last_handled) {
+    MEM_freeN(win->event_last_handled);
+  }
 
   if (win->cursor_keymap_status) {
     MEM_freeN(win->cursor_keymap_status);



More information about the Bf-blender-cvs mailing list