[Bf-blender-cvs] [99e5024e97f] master: UI: Support refreshing menu popups

Julian Eisel noreply at git.blender.org
Tue Nov 1 16:45:37 CET 2022


Commit: 99e5024e97f1c34697885b1a93935e72c019da5c
Author: Julian Eisel
Date:   Tue Nov 1 16:35:23 2022 +0100
Branches: master
https://developer.blender.org/rB99e5024e97f1c34697885b1a93935e72c019da5c

UI: Support refreshing menu popups

This allows us to asynchronously load items into the menu, see
cf985180551d. All menus spawned from Python using the `wm.call_menu`
operator will be affected by this. We could avoid that and only refresh
the menu we need to, but it's worth trying to get this to work as a
general menu feature.

This is a slightly risky change, so keeping an eye open for bugs.

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

M	source/blender/editors/interface/interface_region_menu_popup.cc

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

diff --git a/source/blender/editors/interface/interface_region_menu_popup.cc b/source/blender/editors/interface/interface_region_menu_popup.cc
index 569f657a544..0d19390d508 100644
--- a/source/blender/editors/interface/interface_region_menu_popup.cc
+++ b/source/blender/editors/interface/interface_region_menu_popup.cc
@@ -10,6 +10,7 @@
 #include <cstdarg>
 #include <cstdlib>
 #include <cstring>
+#include <functional>
 
 #include "MEM_guardedalloc.h"
 
@@ -166,28 +167,80 @@ struct uiPopupMenu {
   uiBut *but;
   ARegion *butregion;
 
+  /* Menu hash is created from this, to keep a memory of recently opened menus. */
+  const char *title;
+
   int mx, my;
   bool popup, slideout;
 
-  uiMenuCreateFunc menu_func;
-  void *menu_arg;
+  std::function<void(bContext *C, uiLayout *layout)> menu_func;
 };
 
+/**
+ * \param title: Optional. If set, it will be used to store recently opened menus so they can be
+ *               opened with the mouse over the last chosen entry again.
+ */
+static void ui_popup_menu_create_block(bContext *C,
+                                       uiPopupMenu *pup,
+                                       const char *title,
+                                       const char *block_name)
+{
+  const uiStyle *style = UI_style_get_dpi();
+
+  pup->block = UI_block_begin(C, nullptr, block_name, UI_EMBOSS_PULLDOWN);
+  if (!pup->but) {
+    pup->block->flag |= UI_BLOCK_IS_FLIP | UI_BLOCK_NO_FLIP;
+  }
+  if (title && title[0]) {
+    pup->block->flag |= UI_BLOCK_POPUP_MEMORY;
+    pup->block->puphash = ui_popup_menu_hash(title);
+  }
+  pup->layout = UI_block_layout(
+      pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, UI_MENU_PADDING, style);
+
+  /* NOTE: this intentionally differs from the menu & sub-menu default because many operators
+   * use popups like this to select one of their options -
+   * where having invoke doesn't make sense.
+   * When the menu was opened from a button, use invoke still for compatibility. This used to be
+   * the default and changing now could cause issues. */
+  const wmOperatorCallContext opcontext = pup->but ? WM_OP_INVOKE_REGION_WIN :
+                                                     WM_OP_EXEC_REGION_WIN;
+
+  uiLayoutSetOperatorContext(pup->layout, opcontext);
+
+  if (pup->but) {
+    if (pup->but->context) {
+      uiLayoutContextCopy(pup->layout, pup->but->context);
+    }
+  }
+}
+
 static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, void *arg_pup)
 {
   uiPopupMenu *pup = static_cast<uiPopupMenu *>(arg_pup);
 
-  if (pup->menu_func) {
-    pup->block->handle = handle;
-    pup->menu_func(C, pup->layout, pup->menu_arg);
-    pup->block->handle = nullptr;
+  int minwidth = 0;
+
+  if (!pup->layout) {
+    ui_popup_menu_create_block(C, pup, pup->title, __func__);
+
+    if (pup->menu_func) {
+      pup->block->handle = handle;
+      pup->menu_func(C, pup->layout);
+      pup->block->handle = nullptr;
+    }
+
+    if (uiLayoutGetUnitsX(pup->layout) != 0.0f) {
+      /* Use the minimum width from the layout if it's set. */
+      minwidth = uiLayoutGetUnitsX(pup->layout) * UI_UNIT_X;
+    }
+
+    pup->layout = nullptr;
   }
 
   /* Find block minimum width. */
-  int minwidth;
-  if (uiLayoutGetUnitsX(pup->layout) != 0.0f) {
-    /* Use the minimum width from the layout if it's set. */
-    minwidth = uiLayoutGetUnitsX(pup->layout) * UI_UNIT_X;
+  if (minwidth) {
+    /* Skip. */
   }
   else if (pup->but) {
     /* Minimum width to enforce. */
@@ -236,7 +289,7 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi
   UI_block_flag_enable(block, UI_BLOCK_MOVEMOUSE_QUIT);
 
   if (pup->popup) {
-    int offset[2];
+    int offset[2] = {0, 0};
 
     uiBut *but_activate = nullptr;
     UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_NUMSELECT);
@@ -244,36 +297,42 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi
     UI_block_direction_set(block, direction);
 
     /* offset the mouse position, possibly based on earlier selection */
-    uiBut *bt;
-    if ((block->flag & UI_BLOCK_POPUP_MEMORY) && (bt = ui_popup_menu_memory_get(block))) {
-      /* position mouse on last clicked item, at 0.8*width of the
-       * button, so it doesn't overlap the text too much, also note
-       * the offset is negative because we are inverse moving the
-       * 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
-       * on the first item */
-      offset[0] = 0;
-      LISTBASE_FOREACH (uiBut *, but_iter, &block->buttons) {
-        offset[0] = min_ii(offset[0],
-                           -(but_iter->rect.xmin + 0.8f * BLI_rctf_size_x(&but_iter->rect)));
+    if (!handle->refresh) {
+      uiBut *bt;
+      if ((block->flag & UI_BLOCK_POPUP_MEMORY) && (bt = ui_popup_menu_memory_get(block))) {
+        /* position mouse on last clicked item, at 0.8*width of the
+         * button, so it doesn't overlap the text too much, also note
+         * the offset is negative because we are inverse moving the
+         * 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
+         * on the first item */
+        offset[0] = 0;
+        LISTBASE_FOREACH (uiBut *, but_iter, &block->buttons) {
+          offset[0] = min_ii(offset[0],
+                             -(but_iter->rect.xmin + 0.8f * BLI_rctf_size_x(&but_iter->rect)));
+        }
 
-      offset[1] = 2.1 * UI_UNIT_Y;
+        offset[1] = 2.1 * UI_UNIT_Y;
 
-      LISTBASE_FOREACH (uiBut *, but_iter, &block->buttons) {
-        if (ui_but_is_editable(but_iter)) {
-          but_activate = but_iter;
-          break;
+        LISTBASE_FOREACH (uiBut *, but_iter, &block->buttons) {
+          if (ui_but_is_editable(but_iter)) {
+            but_activate = but_iter;
+            break;
+          }
         }
       }
+      copy_v2_v2_int(handle->prev_bounds_offset, offset);
+    }
+    else {
+      copy_v2_v2_int(offset, handle->prev_bounds_offset);
     }
 
     /* in rare cases this is needed since moving the popup
@@ -312,27 +371,35 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi
   return pup->block;
 }
 
-uiPopupBlockHandle *ui_popup_menu_create(
-    bContext *C, ARegion *butregion, uiBut *but, uiMenuCreateFunc menu_func, void *arg)
+static void ui_block_free_func_POPUP(void *arg_pup)
+{
+  uiPopupMenu *pup = static_cast<uiPopupMenu *>(arg_pup);
+  MEM_delete(pup);
+}
+
+static uiPopupBlockHandle *ui_popup_menu_create(
+    bContext *C,
+    ARegion *butregion,
+    uiBut *but,
+    const char *title,
+    std::function<void(bContext *, uiLayout *)> menu_func)
 {
   wmWindow *window = CTX_wm_window(C);
-  const uiStyle *style = UI_style_get_dpi();
 
-  uiPopupMenu *pup = MEM_cnew<uiPopupMenu>(__func__);
-  pup->block = UI_block_begin(C, nullptr, __func__, UI_EMBOSS_PULLDOWN);
-  pup->block->flag |= UI_BLOCK_NUMSELECT; /* Default menus to numeric-selection. */
-  pup->layout = UI_block_layout(
-      pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, UI_MENU_PADDING, style);
-  pup->slideout = but ? ui_block_is_menu(but->block) : false;
-  pup->but = but;
-  uiLayoutSetOperatorContext(pup->layout, WM_OP_INVOKE_REGION_WIN);
+  uiPopupMenu *pup = MEM_new<uiPopupMenu>(__func__);
+  pup->title = title;
+  /* menu is created from a callback */
+  pup->menu_func = menu_func;
+  if (but) {
+    pup->slideout = ui_block_is_menu(but->block);
+    pup->but = but;
+  }
 
   if (!but) {
     /* no button to start from, means we are a popup */
     pup->mx = window->eventstate->xy[0];
     pup->my = window->eventstate->xy[1];
     pup->popup = true;
-    pup->block->flag |= UI_BLOCK_NO_FLIP;
   }
   /* some enums reversing is strange, currently we have no good way to
    * reverse some enum's but not others, so reverse all so the first menu
@@ -346,17 +413,10 @@ uiPopupBlockHandle *ui_popup_menu_create(
       pup->block->flag |= UI_BLOCK_NO_FLIP;
     }
 #endif
-    if (but->context) {
-      uiLayoutContextCopy(pup->layout, but->context);
-    }
   }
 
-  /* menu is created from a callback */
-  pup->menu_func = menu_func;
-  pup->menu_arg = arg;
-
   uiPopupBlockHandle *handle = ui_popup_block_create(
-      C, butregion, but, nullptr, ui_block_func_POPUP, pup, nullptr);
+      C, butregion, but, nullptr, ui_block_func_POPUP, pup, ui_block_free_func_POPUP);
 
   if (!but) {
     handle->popup = true;
@@ -365,68 +425,73 @@ uiPopupBlockHandle *ui_popup_menu_create(
     WM_event_add_mousemove(window);
   }
 
-  MEM_freeN(pup);
-
   return handle;
 }
 
+uiPopupBlockHandle *ui_popup_menu_create(
+    bContext *C, ARegion *butregion, uiBut *but, uiMenuCreateFunc menu_func, void *arg)
+{
+  return ui_popup_menu_create(
+      C, butregion, but, nullptr, [menu_func, arg](bContext *C, uiLayout *layout) {
+        menu_func(C, layout, arg);
+      });
+}
+
 /** \} */
 
 /* -------------------------------------------------------------------- */
 /** \name Popup Menu API with begin & end
  * \{ */
 
+static void create_title_button(uiLayout *layout, const char *title, int icon)
+{
+  uiBlock *block = uiLayoutGetBlock(layout);
+  char titlestr[256];
+
+  if (icon) {
+    BLI_snprintf(titlestr, sizeof(titlestr), " %s", title);
+    uiDefIconTextBut(block,
+                     UI_BTYPE_LABEL,
+                     0,
+                     icon,
+                     titlestr,
+                     0,
+                     0,
+                     200,
+                     UI_UNIT_Y,
+                     nullptr,
+                     0.0,
+                     0.0,
+                     0,
+                     0,
+                     "");
+  }
+  else {
+ 

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list