Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian Eisel <julian@blender.org>2021-07-13 16:01:00 +0300
committerSybren A. Stüvel <sybren@blender.org>2021-07-15 17:12:36 +0300
commit87c1c8112fa44ccb94a3e996b7499d6577d85d7f (patch)
tree931599775e351a8a5eb78668d3f41625d59507a0
parent8edcb3af963113e37490288876419e8f50d4f1e8 (diff)
UI: Support UI list tooltips, defined via Python scripts
Makes it possible to create tooltips for UI list rows, which can be filled in .py scripts, similar to how they can extend other menus. This is used by the (to be committed) Pose Library add-on to display pose operations (selecting bones of a pose, blending a pose, etc). It's important that the Python scripts check if the UI list is the correct one by checking the list ID. For this to work, a new `bpy.context.ui_list` can be checked. For example, the Pose Library add-on does the following check: ``` def is_pose_asset_view() -> bool: # Important: Must check context first, or the menu is added for every kind of list. list = getattr(context, "ui_list", None) if not list or list.bl_idname != "UI_UL_asset_view" or list.list_id != "pose_assets": return False if not context.asset_handle: return False return True ```
-rw-r--r--release/scripts/startup/bl_ui/__init__.py24
-rw-r--r--source/blender/editors/interface/interface_context_menu.c16
-rw-r--r--source/blender/editors/interface/interface_handlers.c2
-rw-r--r--source/blender/editors/interface/interface_intern.h2
-rw-r--r--source/blender/editors/interface/interface_template_search_menu.c4
-rw-r--r--source/blender/editors/screen/screen_context.c11
-rw-r--r--source/blender/makesrna/intern/rna_ui.c31
-rw-r--r--source/blender/windowmanager/WM_api.h1
-rw-r--r--source/blender/windowmanager/intern/wm_uilist_type.c14
9 files changed, 98 insertions, 7 deletions
diff --git a/release/scripts/startup/bl_ui/__init__.py b/release/scripts/startup/bl_ui/__init__.py
index ef705f8fe37..25484e905c3 100644
--- a/release/scripts/startup/bl_ui/__init__.py
+++ b/release/scripts/startup/bl_ui/__init__.py
@@ -117,13 +117,15 @@ def register():
for cls in mod.classes:
register_class(cls)
- # space_userprefs.py
from bpy.props import (
EnumProperty,
StringProperty,
)
- from bpy.types import WindowManager
+ from bpy.types import (
+ WindowManager,
+ )
+ # space_userprefs.py
def addon_filter_items(_self, _context):
import addon_utils
@@ -234,3 +236,21 @@ class UI_UL_list(bpy.types.UIList):
bpy.utils.register_class(UI_UL_list)
+
+
+class UI_MT_list_item_context_menu(bpy.types.Menu):
+ """
+ UI List item context menu definition. Scripts can append/prepend this to
+ add own operators to the context menu. They must check context though, so
+ their items only draw in a valid context and for the correct UI list.
+ """
+
+ bl_label = "List Item"
+ bl_idname = "UI_MT_list_item_context_menu"
+
+ def draw(self, context):
+ # Dummy function. This type is just for scripts to append their own
+ # context menu items.
+ pass
+
+bpy.utils.register_class(UI_MT_list_item_context_menu)
diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c
index 2a7611eabb1..3049e2bd7b8 100644
--- a/source/blender/editors/interface/interface_context_menu.c
+++ b/source/blender/editors/interface/interface_context_menu.c
@@ -494,7 +494,7 @@ static void ui_but_menu_add_path_operators(uiLayout *layout, PointerRNA *ptr, Pr
RNA_string_set(&props_ptr, "filepath", dir);
}
-bool ui_popup_context_menu_for_button(bContext *C, uiBut *but)
+bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *event)
{
/* ui_but_is_interactive() may let some buttons through that should not get a context menu - it
* doesn't make sense for them. */
@@ -1226,6 +1226,20 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but)
}
}
+ /* UI List item context menu. Scripts can add items to it, by default there's nothing shown. */
+ ARegion *region = CTX_wm_region(C);
+ const bool is_inside_listbox = ui_list_find_mouse_over(region, event) != NULL;
+ const bool is_inside_listrow = is_inside_listbox ?
+ ui_list_row_find_mouse_over(region, event->x, event->y) !=
+ NULL :
+ false;
+ if (is_inside_listrow) {
+ MenuType *mt = WM_menutype_find("UI_MT_list_item_context_menu", true);
+ if (mt) {
+ UI_menutype_draw(C, mt, uiLayoutColumn(layout, false));
+ }
+ }
+
MenuType *mt = WM_menutype_find("WM_MT_button_context", true);
if (mt) {
UI_menutype_draw(C, mt, uiLayoutColumn(layout, false));
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index 2f03539873f..cd975e97613 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -7909,7 +7909,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
if ((event->type == RIGHTMOUSE) && !IS_EVENT_MOD(event, shift, ctrl, alt, oskey) &&
(event->val == KM_PRESS)) {
/* RMB has two options now */
- if (ui_popup_context_menu_for_button(C, but)) {
+ 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 ab76de8bdba..efc94b89046 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -1191,7 +1191,7 @@ struct ARegion *ui_screen_region_find_mouse_over(struct bScreen *screen,
const struct wmEvent *event);
/* interface_context_menu.c */
-bool ui_popup_context_menu_for_button(struct bContext *C, uiBut *but);
+bool ui_popup_context_menu_for_button(struct bContext *C, uiBut *but, const struct wmEvent *event);
void ui_popup_context_menu_for_panel(struct bContext *C,
struct ARegion *region,
struct Panel *panel);
diff --git a/source/blender/editors/interface/interface_template_search_menu.c b/source/blender/editors/interface/interface_template_search_menu.c
index f01dca7712c..3105891142f 100644
--- a/source/blender/editors/interface/interface_template_search_menu.c
+++ b/source/blender/editors/interface/interface_template_search_menu.c
@@ -1037,7 +1037,7 @@ static void menu_search_update_fn(const bContext *UNUSED(C),
static bool ui_search_menu_create_context_menu(struct bContext *C,
void *arg,
void *active,
- const struct wmEvent *UNUSED(event))
+ const struct wmEvent *event)
{
struct MenuSearch_Data *data = arg;
struct MenuSearch_Item *item = active;
@@ -1058,7 +1058,7 @@ static bool ui_search_menu_create_context_menu(struct bContext *C,
CTX_wm_region_set(C, item->wm_context->region);
}
- if (ui_popup_context_menu_for_button(C, but)) {
+ if (ui_popup_context_menu_for_button(C, but, event)) {
has_menu = true;
}
diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c
index ece6ba986f3..3ce2f326dca 100644
--- a/source/blender/editors/screen/screen_context.c
+++ b/source/blender/editors/screen/screen_context.c
@@ -112,6 +112,7 @@ const char *screen_context_dir[] = {
"selected_editable_fcurves",
"active_editable_fcurve",
"selected_editable_keyframes",
+ "ui_list",
"asset_library",
NULL,
};
@@ -1034,6 +1035,15 @@ static eContextResult screen_ctx_asset_library(const bContext *C, bContextDataRe
return CTX_RESULT_OK;
}
+static eContextResult screen_ctx_ui_list(const bContext *C, bContextDataResult *result)
+{
+ wmWindow *win = CTX_wm_window(C);
+ ARegion *region = CTX_wm_region(C);
+ uiList *list = UI_list_find_mouse_over(region, win->eventstate);
+ CTX_data_pointer_set(result, NULL, &RNA_UIList, list);
+ return CTX_RESULT_OK;
+}
+
/* Registry of context callback functions. */
typedef eContextResult (*context_callback)(const bContext *C, bContextDataResult *result);
@@ -1109,6 +1119,7 @@ static void ensure_ed_screen_context_functions(void)
register_context_function("active_editable_fcurve", screen_ctx_active_editable_fcurve);
register_context_function("selected_editable_keyframes", screen_ctx_selected_editable_keyframes);
register_context_function("asset_library", screen_ctx_asset_library);
+ register_context_function("ui_list", screen_ctx_ui_list);
}
/* Entry point for the screen context. */
diff --git a/source/blender/makesrna/intern/rna_ui.c b/source/blender/makesrna/intern/rna_ui.c
index 849bbca16f4..2a076554d03 100644
--- a/source/blender/makesrna/intern/rna_ui.c
+++ b/source/blender/makesrna/intern/rna_ui.c
@@ -458,6 +458,27 @@ static IDProperty **rna_UIList_idprops(PointerRNA *ptr)
return &ui_list->properties;
}
+static void rna_UIList_list_id_get(PointerRNA *ptr, char *value)
+{
+ uiList *ui_list = (uiList *)ptr->data;
+ if (!ui_list->type) {
+ value[0] = '\0';
+ return;
+ }
+
+ strcpy(value, WM_uilisttype_list_id_get(ui_list->type, ui_list));
+}
+
+static int rna_UIList_list_id_length(PointerRNA *ptr)
+{
+ uiList *ui_list = (uiList *)ptr->data;
+ if (!ui_list->type) {
+ return 0;
+ }
+
+ return strlen(WM_uilisttype_list_id_get(ui_list->type, ui_list));
+}
+
static void uilist_draw_item(uiList *ui_list,
bContext *C,
uiLayout *layout,
@@ -1535,6 +1556,16 @@ static void rna_def_uilist(BlenderRNA *brna)
"script, then bl_idname = \"OBJECT_UL_vgroups\")");
/* Data */
+ /* Note that this is the "non-full" list-ID as obtained through #WM_uilisttype_list_id_get(),
+ * which differs from the (internal) `uiList.list_id`. */
+ prop = RNA_def_property(srna, "list_id", PROP_STRING, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_string_funcs(prop, "rna_UIList_list_id_get", "rna_UIList_list_id_length", NULL);
+ RNA_def_property_ui_text(prop,
+ "List Name",
+ "Identifier of the list, if any was passed to the \"list_id\" "
+ "parameter of \"template_list()\"");
+
prop = RNA_def_property(srna, "layout_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_enum_uilist_layout_type_items);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index 601db427381..c529fef73ef 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -624,6 +624,7 @@ void WM_uilisttype_free(void);
void WM_uilisttype_to_full_list_id(const struct uiListType *ult,
const char *list_id,
char *r_ui_list_id);
+const char *WM_uilisttype_list_id_get(const struct uiListType *ult, struct uiList *list);
/* wm_menu_type.c */
void WM_menutype_init(void);
diff --git a/source/blender/windowmanager/intern/wm_uilist_type.c b/source/blender/windowmanager/intern/wm_uilist_type.c
index 434131903fc..6d298ee63f1 100644
--- a/source/blender/windowmanager/intern/wm_uilist_type.c
+++ b/source/blender/windowmanager/intern/wm_uilist_type.c
@@ -21,6 +21,7 @@
*/
#include <stdio.h>
+#include <string.h>
#include "BLI_sys_types.h"
@@ -109,3 +110,16 @@ void WM_uilisttype_to_full_list_id(const uiListType *ult,
/* We tag the list id with the list type... */
BLI_snprintf(r_full_list_id, UI_MAX_NAME_STR, "%s_%s", ult->idname, list_id ? list_id : "");
}
+
+/**
+ * Get the "non-full" list-ID, see #WM_uilisttype_to_full_list_id() for details.
+ *
+ * \note Assumes `uiList.list_id` was set using #WM_uilisttype_to_full_list_id()!
+ */
+const char *WM_uilisttype_list_id_get(const uiListType *ult, uiList *list)
+{
+ /* Some sanity check for the assumed behavior of #WM_uilisttype_to_full_list_id(). */
+ BLI_assert((list->list_id + strlen(ult->idname))[0] == '_');
+ /* +1 to skip the '_' */
+ return list->list_id + strlen(ult->idname) + 1;
+}