Tool System: support tool description for tips
authorCampbell Barton <ideasman42@gmail.com>
Fri, 31 Aug 2018 04:37:10 +0000 (14:37 +1000)
committerCampbell Barton <ideasman42@gmail.com>
Fri, 31 Aug 2018 04:37:10 +0000 (14:37 +1000)
When the description isn't set, the operators is used instead.

release/scripts/startup/bl_ui/space_toolsystem_common.py
release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
source/blender/editors/interface/interface_region_tooltip.c

index b8e3d81c1d4b76aced1af803b679e0e015a20b66..1be86ea7c1c0ea4559390279d3eb68023f243677 100644 (file)
@@ -23,8 +23,13 @@ from bpy.types import (
 )
 
 __all__ = (
-    "ToolSelectPanelHelper",
     "ToolDef",
+    "ToolSelectPanelHelper",
+    "activate_by_name",
+    "activate_by_name_or_cycle",
+    "description_from_name",
+    "keymap_from_name",
+    "keymap_from_context",
 )
 
 # Support reloading icons.
@@ -70,6 +75,8 @@ ToolDef = namedtuple(
     (
         # The name to display in the interface.
         "text",
+        # Description (for tooltip), when not set, use the description of 'operator'.
+        "description",
         # The name of the icon to use (found in ``release/datafiles/icons``) or None for no icon.
         "icon",
         # An optional cursor to use when this tool is active.
@@ -104,6 +111,7 @@ def from_dict(kw_args):
     (since keymap is a callback).
     """
     kw = {
+        "description": None,
         "icon": None,
         "cursor": None,
         "widget": None,
@@ -631,6 +639,44 @@ def activate_by_name_or_cycle(context, space_type, text, offset=1):
     return True
 
 
+def description_from_name(context, space_type, text, *, use_operator=True):
+    # Used directly for tooltips.
+    cls, item, index = ToolSelectPanelHelper._tool_get_by_name(context, space_type, text)
+    if item is None:
+        return False
+
+    # Custom description.
+    description = item.description
+    if description is not None:
+        return description
+
+    # Extract from the operator.
+    if use_operator:
+        operator = item.operator
+
+        if operator is None:
+            if item.keymap is not None:
+                operator = item.keymap[0].keymap_items[0].idname
+
+        if operator is not None:
+            import _bpy
+            return _bpy.ops.get_rna(operator).bl_rna.description
+    return ""
+
+
+def keymap_from_name(context, space_type, text):
+    # Used directly for tooltips.
+    cls, item, index = ToolSelectPanelHelper._tool_get_by_name(context, space_type, text)
+    if item is None:
+        return False
+
+    keymap = item.keymap
+    # List container of one.
+    if keymap:
+        return keymap[0]
+    return ""
+
+
 def keymap_from_context(context, space_type):
     """
     Keymap for popup toolbar, currently generated each time.
index ae8c7d82f9a46eb4aae201e9cef54b127ab6c693..003f1fa4a7a31b4cb61fe76d61130e6a1d19ed5d 100644 (file)
@@ -163,6 +163,9 @@ class _defs_view3d_generic:
 
         return dict(
             text="Cursor",
+            description=(
+                "Set the 3D cursor location, drag to transform"
+            ),
             icon="ops.generic.cursor",
             keymap=(
                 ("view3d.cursor3d", dict(), dict(type='ACTIONMOUSE', value='PRESS')),
@@ -369,6 +372,9 @@ class _defs_transform:
 
         return dict(
             text="Transform",
+            description=(
+                "Supports any combination of grab, rotate & scale at once"
+            ),
             icon="ops.transform.transform",
             widget="TRANSFORM_GGT_gizmo",
             # No keymap default action, only for gizmo!
index 97f501b7448fcc876119f0867fd6842b9ddab9dc..ae6ab09a62ade77ad9d6f5e318f64096dfe5e45d 100644 (file)
 #include "BLF_api.h"
 #include "BLT_translation.h"
 
+#ifdef WITH_PYTHON
+#  include "BPY_extern.h"
+#endif
+
 #include "ED_screen.h"
 
 #include "interface_intern.h"
@@ -306,13 +310,13 @@ static void ui_tooltip_region_free_cb(ARegion *ar)
 /** \name ToolTip Creation
  * \{ */
 
-static uiTooltipData *ui_tooltip_data_from_keymap(bContext *C, wmKeyMap *keymap)
+static bool ui_tooltip_data_append_from_keymap(
+        bContext *C, uiTooltipData *data,
+        wmKeyMap *keymap)
 {
+       const int fields_len_init = data->fields_len;
        char buf[512];
 
-       /* create tooltip data */
-       uiTooltipData *data = MEM_callocN(sizeof(uiTooltipData), "uiTooltipData");
-
        for (wmKeyMapItem *kmi = keymap->items.first; kmi; kmi = kmi->next) {
                wmOperatorType *ot = WM_operatortype_find(kmi->idname, true);
                if (ot != NULL) {
@@ -341,7 +345,7 @@ static uiTooltipData *ui_tooltip_data_from_keymap(bContext *C, wmKeyMap *keymap)
                        }
 
                        /* Python */
-                       {
+                       if (U.flag & USER_TOOLTIPS_PYTHON) {
                                uiTooltipField *field = text_field_add(
                                        data, &(uiTooltipFormat){
                                            .style = UI_TIP_STYLE_NORMAL,
@@ -354,6 +358,88 @@ static uiTooltipData *ui_tooltip_data_from_keymap(bContext *C, wmKeyMap *keymap)
                        }
                }
        }
+
+       return (fields_len_init != data->fields_len);
+}
+
+
+/**
+ * Special tool-system exception.
+ */
+static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but)
+{
+       if (but->optype == NULL) {
+               return NULL;
+       }
+
+       if (!STREQ(but->optype->idname, "WM_OT_tool_set_by_name")) {
+               return NULL;
+       }
+
+       char tool_name[MAX_NAME];
+       RNA_string_get(but->opptr, "name", tool_name);
+       BLI_assert(tool_name[0] != '\0');
+
+       /* We have a tool, now extract the info. */
+       uiTooltipData *data = MEM_callocN(sizeof(uiTooltipData), "uiTooltipData");
+
+#ifdef WITH_PYTHON
+       /* it turns out to be most simple to do this via Python since C
+        * doesn't have access to information about non-active tools.
+        */
+       char expr[256];
+
+       /* Tip */
+       {
+               SNPRINTF(
+                       expr,
+                       "__import__('bl_ui').space_toolsystem_common.description_from_name("
+                       "__import__('bpy').context, "
+                       "__import__('bpy').context.space_data.type, "
+                       "'%s') + '.'",
+                       tool_name);
+
+               char *expr_result = NULL;
+               if (BPY_execute_string_as_string(C, expr, true, &expr_result)) {
+                       if (!STREQ(expr_result, ".")) {
+                               uiTooltipField *field = text_field_add(
+                                       data, &(uiTooltipFormat){
+                                           .style = UI_TIP_STYLE_NORMAL,
+                                           .color_id = UI_TIP_LC_MAIN,
+                                           .is_pad = true,
+                                       });
+                               field->text = expr_result;
+                       }
+                       else {
+                               MEM_freeN(expr_result);
+                       }
+               }
+       }
+
+       /* Keymap */
+
+       /* This is too handy not to expose somehow, let's be sneaky for now. */
+       if (CTX_wm_window(C)->eventstate->shift) {
+               SNPRINTF(
+                       expr,
+                       "getattr("
+                       "__import__('bl_ui').space_toolsystem_common.keymap_from_name("
+                       "__import__('bpy').context, "
+                       "__import__('bpy').context.space_data.type, "
+                       "'%s'), "
+                       "'as_pointer', lambda: 0)()",
+                       tool_name);
+
+               intptr_t expr_result = 0;
+               if (BPY_execute_string_as_intptr(C, expr, true, &expr_result)) {
+                       if (expr_result != 0) {
+                               wmKeyMap *keymap = (wmKeyMap *)expr_result;
+                               ui_tooltip_data_append_from_keymap(C, data, keymap);
+                       }
+               }
+       }
+#endif  /* WITH_PYTHON */
+
        if (data->fields_len == 0) {
                MEM_freeN(data);
                return NULL;
@@ -894,31 +980,14 @@ ARegion *UI_tooltip_create_from_button(bContext *C, ARegion *butregion, uiBut *b
        }
        uiTooltipData *data = NULL;
 
-       /* custom tips for pre-defined operators */
-       if (but->optype) {
-               /* TODO(campbell): we now use 'WM_OT_tool_set_by_name', this logic will be moved into the status bar. */
-               if (false && STREQ(but->optype->idname, "WM_OT_tool_set")) {
-                       char keymap[64] = "";
-                       RNA_string_get(but->opptr, "keymap", keymap);
-                       if (keymap[0]) {
-                               ScrArea *sa = CTX_wm_area(C);
-                               /* It happens in rare cases, for tooltips originated from the toolbar.
-                                * It is hard to reproduce, but it happens when the mouse is nowhere near the actual tool. */
-                               if (sa == NULL) {
-                                       return NULL;
-                               }
-                               wmKeyMap *km = WM_keymap_find_all(C, keymap, sa->spacetype, RGN_TYPE_WINDOW);
-                               if (km != NULL) {
-                                       data = ui_tooltip_data_from_keymap(C, km);
-                               }
-                       }
-               }
+       if (data == NULL) {
+               data = ui_tooltip_data_from_tool(C, but);
        }
-       /* toolsystem exception */
 
        if (data == NULL) {
                data = ui_tooltip_data_from_button(C, but);
        }
+
        if (data == NULL) {
                return NULL;
        }