[Bf-blender-cvs] [8839b4c32a4] master: UI: support persistent state during number/slider interaction

Campbell Barton noreply at git.blender.org
Tue Jul 13 12:04:38 CEST 2021


Commit: 8839b4c32a44ba2c50f08ccd815d450ac71c454a
Author: Campbell Barton
Date:   Thu Jul 8 16:23:41 2021 +1000
Branches: master
https://developer.blender.org/rB8839b4c32a44ba2c50f08ccd815d450ac71c454a

UI: support persistent state during number/slider interaction

Support for begin/update/end callbacks allowing state to be cached
and reused while dragging a number button or slider.

This is done using `UI_block_interaction_set` to set callbacks.

- Dragging multiple buttons at once is supported,
  passing multiple unique events into the update function.

- Update is only called once even when multiple buttons are edited.

- The update callback can detect the difference between click & drag
  actions so situations to support skipping cache creation and
  freeing for situations where it's not beneficial.

Reviewed by: Severin, HooglyBoogly

Ref D11861

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

M	source/blender/editors/include/UI_interface.h
M	source/blender/editors/interface/interface_handlers.c
M	source/blender/editors/interface/interface_intern.h

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

diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index 6901176a587..802c175492f 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -58,6 +58,7 @@ struct bNodeTree;
 struct bScreen;
 struct rctf;
 struct rcti;
+struct uiBlockInteraction_Handle;
 struct uiButSearch;
 struct uiFontStyle;
 struct uiList;
@@ -514,6 +515,54 @@ typedef int (*uiButPushedStateFunc)(struct uiBut *but, const void *arg);
 
 typedef void (*uiBlockHandleFunc)(struct bContext *C, void *arg, int event);
 
+/* -------------------------------------------------------------------- */
+/** \name Custom Interaction
+ *
+ * Sometimes it's useful to create data that remains available
+ * while the user interacts with a button.
+ *
+ * A common case is dragging a number button or slider
+ * however this could be used in other cases too.
+ * \{ */
+
+struct uiBlockInteraction_Params {
+  /**
+   * When true, this interaction is not modal
+   * (user clicking on a number button arrows or pasting a value for example).
+   */
+  bool is_click;
+  /**
+   * Array of unique event ID's (values from #uiBut.retval).
+   * There may be more than one for multi-button editing (see #UI_BUT_DRAG_MULTI).
+   */
+  int *unique_retval_ids;
+  uint unique_retval_ids_len;
+};
+
+/** Returns 'user_data', freed by #uiBlockInteractionEndFn. */
+typedef void *(*uiBlockInteractionBeginFn)(struct bContext *C,
+                                           const struct uiBlockInteraction_Params *params,
+                                           void *arg1);
+typedef void (*uiBlockInteractionEndFn)(struct bContext *C,
+                                        const struct uiBlockInteraction_Params *params,
+                                        void *arg1,
+                                        void *user_data);
+typedef void (*uiBlockInteractionUpdateFn)(struct bContext *C,
+                                           const struct uiBlockInteraction_Params *params,
+                                           void *arg1,
+                                           void *user_data);
+
+typedef struct uiBlockInteraction_CallbackData {
+  uiBlockInteractionBeginFn begin_fn;
+  uiBlockInteractionEndFn end_fn;
+  uiBlockInteractionUpdateFn update_fn;
+  void *arg1;
+} uiBlockInteraction_CallbackData;
+
+void UI_block_interaction_set(uiBlock *block, uiBlockInteraction_CallbackData *callbacks);
+
+/** \} */
+
 /* Menu Callbacks */
 
 typedef void (*uiMenuCreateFunc)(struct bContext *C, struct uiLayout *layout, void *arg1);
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index 743f646d4df..5d3e9e42f29 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -35,10 +35,12 @@
 #include "DNA_scene_types.h"
 #include "DNA_screen_types.h"
 
+#include "BLI_array_utils.h"
 #include "BLI_linklist.h"
 #include "BLI_listbase.h"
 #include "BLI_math.h"
 #include "BLI_rect.h"
+#include "BLI_sort_utils.h"
 #include "BLI_string.h"
 #include "BLI_string_cursor_utf8.h"
 #include "BLI_string_utf8.h"
@@ -170,6 +172,20 @@ static bool ui_but_find_select_in_enum__cmp(const uiBut *but_a, const uiBut *but
 static void ui_textedit_string_set(uiBut *but, struct uiHandleButtonData *data, const char *str);
 static void button_tooltip_timer_reset(bContext *C, uiBut *but);
 
+static void ui_block_interaction_begin_ensure(bContext *C,
+                                              uiBlock *block,
+                                              struct uiHandleButtonData *data,
+                                              const bool is_click);
+static struct uiBlockInteraction_Handle *ui_block_interaction_begin(struct bContext *C,
+                                                                    uiBlock *block,
+                                                                    const bool is_click);
+static void ui_block_interaction_end(struct bContext *C,
+                                     uiBlockInteraction_CallbackData *callbacks,
+                                     struct uiBlockInteraction_Handle *interaction);
+static void ui_block_interaction_update(struct bContext *C,
+                                        uiBlockInteraction_CallbackData *callbacks,
+                                        struct uiBlockInteraction_Handle *interaction);
+
 #ifdef USE_KEYNAV_LIMIT
 static void ui_mouse_motion_keynav_init(struct uiKeyNavLock *keynav, const wmEvent *event);
 static bool ui_mouse_motion_keynav_test(struct uiKeyNavLock *keynav, const wmEvent *event);
@@ -225,6 +241,19 @@ typedef enum uiMenuScrollType {
   MENU_SCROLL_BOTTOM,
 } uiMenuScrollType;
 
+typedef struct uiBlockInteraction_Handle {
+  struct uiBlockInteraction_Params params;
+  void *user_data;
+  /**
+   * This is shared between #uiHandleButtonData and #uiAfterFunc,
+   * the last user runs the end callback and frees the data.
+   *
+   * This is needed as the order of freeing changes depending on
+   * accepting/canceling the operation.
+   */
+  int user_count;
+} uiBlockInteraction_Handle;
+
 #ifdef USE_ALLSELECT
 
 /* Unfortunately there's no good way handle more generally:
@@ -430,6 +459,8 @@ typedef struct uiHandleButtonData {
   uiSelectContextStore select_others;
 #endif
 
+  struct uiBlockInteraction_Handle *custom_interaction_handle;
+
   /* Text field undo. */
   struct uiUndoStack_Text *undo_stack_text;
 
@@ -471,6 +502,9 @@ typedef struct uiAfterFunc {
   void *search_arg;
   uiFreeArgFunc search_arg_free_fn;
 
+  uiBlockInteraction_CallbackData custom_interaction_callbacks;
+  uiBlockInteraction_Handle *custom_interaction_handle;
+
   bContextStore *context;
 
   char undostr[BKE_UNDO_STR_MAX];
@@ -827,6 +861,27 @@ static void ui_apply_but_func(bContext *C, uiBut *but)
       search_but->arg = NULL;
     }
 
+    if (but->active != NULL) {
+      uiHandleButtonData *data = but->active;
+      if (data->custom_interaction_handle != NULL) {
+        after->custom_interaction_callbacks = block->custom_interaction_callbacks;
+        after->custom_interaction_handle = data->custom_interaction_handle;
+
+        /* Ensure this callback runs once and last. */
+        uiAfterFunc *after_prev = after->prev;
+        if (after_prev &&
+            (after_prev->custom_interaction_handle == data->custom_interaction_handle)) {
+          after_prev->custom_interaction_handle = NULL;
+          memset(&after_prev->custom_interaction_callbacks,
+                 0x0,
+                 sizeof(after_prev->custom_interaction_callbacks));
+        }
+        else {
+          after->custom_interaction_handle->user_count++;
+        }
+      }
+    }
+
     if (but->context) {
       after->context = CTX_store_copy(but->context);
     }
@@ -997,6 +1052,18 @@ static void ui_apply_but_funcs_after(bContext *C)
       after.search_arg_free_fn(after.search_arg);
     }
 
+    if (after.custom_interaction_handle != NULL) {
+      after.custom_interaction_handle->user_count--;
+      BLI_assert(after.custom_interaction_handle->user_count >= 0);
+      if (after.custom_interaction_handle->user_count == 0) {
+        ui_block_interaction_update(
+            C, &after.custom_interaction_callbacks, after.custom_interaction_handle);
+        ui_block_interaction_end(
+            C, &after.custom_interaction_callbacks, after.custom_interaction_handle);
+      }
+      after.custom_interaction_handle = NULL;
+    }
+
     ui_afterfunc_update_preferences_dirty(&after);
 
     if (after.undostr[0]) {
@@ -2283,6 +2350,11 @@ static void ui_apply_but(
     uiButCurveProfile *but_profile = (uiButCurveProfile *)but;
     but_profile->edit_profile = editprofile;
   }
+
+  if (data->custom_interaction_handle != NULL) {
+    ui_block_interaction_update(
+        C, &block->custom_interaction_callbacks, data->custom_interaction_handle);
+  }
 }
 
 /** \} */
@@ -4852,6 +4924,8 @@ static bool ui_numedit_but_NUM(uiButNumber *number_but,
     return changed;
   }
 
+  ui_block_interaction_begin_ensure(but->block->evil_C, but->block, data, false);
+
   if (ui_but_is_cursor_warp(but)) {
     const float softmin = but->softmin;
     const float softmax = but->softmax;
@@ -5362,6 +5436,8 @@ static bool ui_numedit_but_SLI(uiBut *but,
     return changed;
   }
 
+  ui_block_interaction_begin_ensure(but->block->evil_C, but->block, data, false);
+
   const PropertyScaleType scale_type = ui_but_scale_type(but);
 
   softmin = but->softmin;
@@ -8239,6 +8315,16 @@ static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState s
     but->flag &= ~UI_SELECT;
   }
 
+  if (state == BUTTON_STATE_TEXT_EDITING) {
+    ui_block_interaction_begin_ensure(C, but->block, data, true);
+  }
+  else if (state == BUTTON_STATE_EXIT) {
+    if (data->state == BUTTON_STATE_NUM_EDITING) {
+      /* This happens on pasting values for example. */
+      ui_block_interaction_begin_ensure(C, but->block, data, true);
+    }
+  }
+
   data->state = state;
 
   if (state != BUTTON_STATE_EXIT) {
@@ -8467,6 +8553,21 @@ static void button_activate_exit(
   ED_region_tag_redraw_no_rebuild(data->region);
   ED_region_tag_refresh_ui(data->region);
 
+  if ((but->flag & UI_BUT_DRAG_MULTI) == 0) {
+    if (data->custom_interaction_handle != NULL) {
+      /* Should only set when the button is modal. */
+      BLI_assert(but->active != NULL);
+      data->custom_interaction_handle->user_count--;
+
+      BLI_assert(data->custom_interaction_handle->user_count >= 0);
+      if (data->custom_interaction_handle->user_count == 0) {
+        ui_block_interaction_end(
+            C, &but->block->custom_interaction_callbacks, data->custom_interaction_handle);
+      }
+      data->custom_interaction_handle = NULL;
+    }
+  }
+
   /* clean up button */
   if (but->active) {
     MEM_freeN(but->active);
@@ -11314,3 +11415,100 @@ bool UI_but_active_drop_color(bContext *C)
 }
 
 /** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name UI Block Interaction API
+ * \{ */
+
+void UI_block_interaction_set(uiBlock *block, uiBlockInteraction_Call

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list