[Bf-blender-cvs] [17c928e9752] master: UI: Add context menu support for tree-view items

Julian Eisel noreply at git.blender.org
Fri Oct 8 20:12:28 CEST 2021


Commit: 17c928e9752372b698a1ed27e243181873aa411e
Author: Julian Eisel
Date:   Fri Oct 8 19:56:24 2021 +0200
Branches: master
https://developer.blender.org/rB17c928e9752372b698a1ed27e243181873aa411e

UI: Add context menu support for tree-view items

Tree-view items can now easily define their own context menu. This works
by overriding the `ui::AbstractTreeViewItem::build_context_menu()`
function. See the documentation:
https://wiki.blender.org/wiki/Source/Interface/Views#Context_Menus

Consistently with the Outliner and File Browser, the right-clicked item
also gets activated. This makes sure the correct context is set for the
operators and makes it clear to the user which item is operated on.

An operator to rename the active item is also added, which is something
you'd typically want to put in the context menu as well.

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

M	source/blender/editors/include/UI_interface.h
M	source/blender/editors/include/UI_tree_view.hh
M	source/blender/editors/interface/interface_context_menu.c
M	source/blender/editors/interface/interface_handlers.c
M	source/blender/editors/interface/interface_intern.h
M	source/blender/editors/interface/interface_ops.c
M	source/blender/editors/interface/interface_query.c
M	source/blender/editors/interface/interface_view.cc
M	source/blender/editors/interface/tree_view.cc

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

diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index c536eff771d..ddd5e77cbb6 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -2783,7 +2783,12 @@ char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item,
 bool UI_tree_view_item_can_rename(const uiTreeViewItemHandle *item_handle);
 void UI_tree_view_item_begin_rename(uiTreeViewItemHandle *item_handle);
 
+void UI_tree_view_item_context_menu_build(struct bContext *C,
+                                          const uiTreeViewItemHandle *item,
+                                          uiLayout *column);
+
 uiTreeViewItemHandle *UI_block_tree_view_find_item_at(const struct ARegion *region, int x, int y);
+uiTreeViewItemHandle *UI_block_tree_view_find_active_item(const struct ARegion *region);
 
 #ifdef __cplusplus
 }
diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh
index 272439a2ae9..ae85375ed2f 100644
--- a/source/blender/editors/include/UI_tree_view.hh
+++ b/source/blender/editors/include/UI_tree_view.hh
@@ -234,6 +234,7 @@ class AbstractTreeViewItem : public TreeViewItemContainer {
   virtual ~AbstractTreeViewItem() = default;
 
   virtual void build_row(uiLayout &row) = 0;
+  virtual void build_context_menu(bContext &C, uiLayout &column) const;
 
   virtual void on_activate();
   /**
diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c
index bb9e813ea50..d327124484b 100644
--- a/source/blender/editors/interface/interface_context_menu.c
+++ b/source/blender/editors/interface/interface_context_menu.c
@@ -925,6 +925,18 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *ev
     }
   }
 
+  {
+    const ARegion *region = CTX_wm_region(C);
+    uiButTreeRow *treerow_but = (uiButTreeRow *)ui_tree_row_find_mouse_over(
+        region, event->x, event->y);
+    if (treerow_but) {
+      BLI_assert(treerow_but->but.type == UI_BTYPE_TREEROW);
+      UI_tree_view_item_context_menu_build(
+          C, treerow_but->tree_item, uiLayoutColumn(layout, false));
+      uiItemS(layout);
+    }
+  }
+
   /* If the button represents an id, it can set the "id" context pointer. */
   if (U.experimental.use_extended_asset_browser && ED_asset_can_mark_single_from_context(C)) {
     ID *id = CTX_data_pointer_get_type(C, "id", &RNA_ID).data;
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index bf9b37c00fa..e1f8d18ce35 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -7961,6 +7961,14 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
     /* handle menu */
     if ((event->type == RIGHTMOUSE) && !IS_EVENT_MOD(event, shift, ctrl, alt, oskey) &&
         (event->val == KM_PRESS)) {
+      /* For some button types that are typically representing entire sets of data, right-clicking
+       * to spawn the context menu should also activate the item. This makes it clear which item
+       * will be operated on.
+       * Apply the button immediately, so context menu polls get the right active item. */
+      if (ELEM(but->type, UI_BTYPE_TREEROW)) {
+        ui_apply_but(C, but->block, but, but->active, true);
+      }
+
       /* RMB has two options now */
       if (ui_popup_context_menu_for_button(C, but, event)) {
         return WM_UI_HANDLER_BREAK;
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index e7a728efce1..c7781d65058 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -1177,6 +1177,7 @@ uiBut *ui_list_row_find_from_index(const struct ARegion *region,
                                    const int index,
                                    uiBut *listbox) ATTR_WARN_UNUSED_RESULT;
 uiBut *ui_tree_row_find_mouse_over(const struct ARegion *region, const int x, const int y);
+uiBut *ui_tree_row_find_active(const struct ARegion *region);
 
 typedef bool (*uiButFindPollFn)(const uiBut *but, const void *customdata);
 uiBut *ui_but_find_mouse_over_ex(const struct ARegion *region,
diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c
index 1fc07bce341..423950d4dbd 100644
--- a/source/blender/editors/interface/interface_ops.c
+++ b/source/blender/editors/interface/interface_ops.c
@@ -1962,6 +1962,49 @@ static void UI_OT_tree_view_drop(wmOperatorType *ot)
 
 /** \} */
 
+/* -------------------------------------------------------------------- */
+/** \name UI Tree-View Item Rename Operator
+ *
+ * General purpose renaming operator for tree-views. Thanks to this, to add a rename button to
+ * context menus for example, tree-view API users don't have to implement own renaming operators
+ * with the same logic as they already have for their #ui::AbstractTreeViewItem::rename() override.
+ *
+ * \{ */
+
+static bool ui_tree_view_item_rename_poll(bContext *C)
+{
+  const ARegion *region = CTX_wm_region(C);
+  const uiTreeViewItemHandle *active_item = UI_block_tree_view_find_active_item(region);
+  return active_item != NULL && UI_tree_view_item_can_rename(active_item);
+}
+
+static int ui_tree_view_item_rename_exec(bContext *C, wmOperator *UNUSED(op))
+{
+  ARegion *region = CTX_wm_region(C);
+  uiTreeViewItemHandle *active_item = UI_block_tree_view_find_active_item(region);
+
+  UI_tree_view_item_begin_rename(active_item);
+  ED_region_tag_redraw(region);
+
+  return OPERATOR_FINISHED;
+}
+
+static void UI_OT_tree_view_item_rename(wmOperatorType *ot)
+{
+  ot->name = "Rename Tree-View Item";
+  ot->idname = "UI_OT_tree_view_item_rename";
+  ot->description = "Rename the active item in the tree";
+
+  ot->exec = ui_tree_view_item_rename_exec;
+  ot->poll = ui_tree_view_item_rename_poll;
+  /* Could get a custom tooltip via the `get_description()` callback and another overridable
+   * function of the tree-view. */
+
+  ot->flag = OPTYPE_INTERNAL;
+}
+
+/** \} */
+
 /* -------------------------------------------------------------------- */
 /** \name Operator & Keymap Registration
  * \{ */
@@ -1990,6 +2033,7 @@ void ED_operatortypes_ui(void)
   WM_operatortype_append(UI_OT_list_start_filter);
 
   WM_operatortype_append(UI_OT_tree_view_drop);
+  WM_operatortype_append(UI_OT_tree_view_item_rename);
 
   /* external */
   WM_operatortype_append(UI_OT_eyedropper_color);
diff --git a/source/blender/editors/interface/interface_query.c b/source/blender/editors/interface/interface_query.c
index 2f6bda3252d..8674f1e435a 100644
--- a/source/blender/editors/interface/interface_query.c
+++ b/source/blender/editors/interface/interface_query.c
@@ -473,6 +473,21 @@ uiBut *ui_tree_row_find_mouse_over(const ARegion *region, const int x, const int
   return ui_but_find_mouse_over_ex(region, x, y, false, ui_but_is_treerow, NULL);
 }
 
+static bool ui_but_is_active_treerow(const uiBut *but, const void *customdata)
+{
+  if (!ui_but_is_treerow(but, customdata)) {
+    return false;
+  }
+
+  const uiButTreeRow *treerow_but = (const uiButTreeRow *)but;
+  return UI_tree_view_item_is_active(treerow_but->tree_item);
+}
+
+uiBut *ui_tree_row_find_active(const ARegion *region)
+{
+  return ui_but_find(region, ui_but_is_active_treerow, NULL);
+}
+
 /** \} */
 
 /* -------------------------------------------------------------------- */
diff --git a/source/blender/editors/interface/interface_view.cc b/source/blender/editors/interface/interface_view.cc
index 8122b965892..fdd8eb0cc71 100644
--- a/source/blender/editors/interface/interface_view.cc
+++ b/source/blender/editors/interface/interface_view.cc
@@ -94,6 +94,16 @@ uiTreeViewItemHandle *UI_block_tree_view_find_item_at(const ARegion *region,
   return tree_row_but->tree_item;
 }
 
+uiTreeViewItemHandle *UI_block_tree_view_find_active_item(const ARegion *region)
+{
+  uiButTreeRow *tree_row_but = (uiButTreeRow *)ui_tree_row_find_active(region);
+  if (!tree_row_but) {
+    return nullptr;
+  }
+
+  return tree_row_but->tree_item;
+}
+
 static StringRef ui_block_view_find_idname(const uiBlock &block, const AbstractTreeView &view)
 {
   /* First get the idname the of the view we're looking for. */
diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc
index 3f66810b7f6..3f3a8c5bce5 100644
--- a/source/blender/editors/interface/tree_view.cc
+++ b/source/blender/editors/interface/tree_view.cc
@@ -369,6 +369,11 @@ bool AbstractTreeViewItem::rename(StringRefNull new_name)
   return true;
 }
 
+void AbstractTreeViewItem::build_context_menu(bContext & /*C*/, uiLayout & /*column*/) const
+{
+  /* No context menu by default. */
+}
+
 void AbstractTreeViewItem::update_from_old(const AbstractTreeViewItem &old)
 {
   is_open_ = old.is_open_;
@@ -707,3 +712,11 @@ void UI_tree_view_item_begin_rename(uiTreeViewItemHandle *item_handle)
   AbstractTreeViewItem &item = reinterpret_cast<AbstractTreeViewItem &>(*item_handle);
   item.begin_renaming();
 }
+
+void UI_tree_view_item_context_menu_build(bContext *C,
+                                          const uiTreeViewItemHandle *item_handle,
+                                          uiLayout *column)
+{
+  const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_handle);
+  item.build_context_menu(*C, *column);
+}



More information about the Bf-blender-cvs mailing list