Merge branch 'master' into blender2.8
[blender.git] / source / blender / editors / interface / interface_region_tooltip.c
index 7bb0a02bbf686458e0174bc20326fbc081cc94ec..6f4e48a9793ffe5b01e61da9044626d4e405a317 100644 (file)
@@ -45,6 +45,7 @@
 #include "MEM_guardedalloc.h"
 
 #include "DNA_userdef_types.h"
+#include "DNA_brush_types.h"
 
 #include "BLI_math.h"
 #include "BLI_string.h"
@@ -54,6 +55,7 @@
 
 #include "BKE_context.h"
 #include "BKE_screen.h"
+#include "BKE_library.h"
 
 #include "WM_api.h"
 #include "WM_types.h"
 #include "BLF_api.h"
 #include "BLT_translation.h"
 
+#ifdef WITH_PYTHON
+#  include "BPY_extern.h"
+#endif
+
 #include "ED_screen.h"
 
 #include "interface_intern.h"
@@ -164,6 +170,7 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *ar)
        uiWidgetColors *theme = ui_tooltip_get_theme();
        rcti bbox = data->bbox;
        float tip_colors[UI_TIP_LC_MAX][3];
+       unsigned char drawcol[4] = {0, 0, 0, 255}; /* to store color in while drawing (alpha is always 255) */
 
        float *main_color    = tip_colors[UI_TIP_LC_MAIN]; /* the color from the theme */
        float *value_color   = tip_colors[UI_TIP_LC_VALUE];
@@ -174,12 +181,7 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *ar)
 
        float background_color[3];
        float tone_bg;
-       int i, multisample_enabled;
-
-       /* disable AA, makes widgets too blurry */
-       multisample_enabled = glIsEnabled(GL_MULTISAMPLE);
-       if (multisample_enabled)
-               glDisable(GL_MULTISAMPLE);
+       int i;
 
        wmOrtho2_region_pixelspace(ar);
 
@@ -232,9 +234,9 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *ar)
                        fstyle_header.shadowalpha = 1.0f;
                        fstyle_header.word_wrap = true;
 
+                       rgb_float_to_uchar(drawcol, tip_colors[UI_TIP_LC_MAIN]);
                        UI_fontstyle_set(&fstyle_header);
-                       glColor3fv(tip_colors[UI_TIP_LC_MAIN]);
-                       UI_fontstyle_draw(&fstyle_header, &bbox, field->text);
+                       UI_fontstyle_draw(&fstyle_header, &bbox, field->text, drawcol);
 
                        fstyle_header.shadow = 0;
 
@@ -245,8 +247,8 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *ar)
                                bbox.xmin += xofs;
                                bbox.ymax -= yofs;
 
-                               glColor3fv(tip_colors[UI_TIP_LC_ACTIVE]);
-                               UI_fontstyle_draw(&fstyle_header, &bbox, field->text_suffix);
+                               rgb_float_to_uchar(drawcol, tip_colors[UI_TIP_LC_ACTIVE]);
+                               UI_fontstyle_draw(&fstyle_header, &bbox, field->text_suffix, drawcol);
 
                                /* undo offset */
                                bbox.xmin -= xofs;
@@ -261,8 +263,8 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *ar)
                        UI_fontstyle_set(&fstyle_mono);
                        /* XXX, needed because we dont have mono in 'U.uifonts' */
                        BLF_size(fstyle_mono.uifont_id, fstyle_mono.points * U.pixelsize, U.dpi);
-                       glColor3fv(tip_colors[field->format.color_id]);
-                       UI_fontstyle_draw(&fstyle_mono, &bbox, field->text);
+                       rgb_float_to_uchar(drawcol, tip_colors[field->format.color_id]);
+                       UI_fontstyle_draw(&fstyle_mono, &bbox, field->text, drawcol);
                }
                else {
                        uiFontStyle fstyle_normal = data->fstyle;
@@ -270,9 +272,9 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *ar)
                        fstyle_normal.word_wrap = true;
 
                        /* draw remaining data */
+                       rgb_float_to_uchar(drawcol, tip_colors[field->format.color_id]);
                        UI_fontstyle_set(&fstyle_normal);
-                       glColor3fv(tip_colors[field->format.color_id]);
-                       UI_fontstyle_draw(&fstyle_normal, &bbox, field->text);
+                       UI_fontstyle_draw(&fstyle_normal, &bbox, field->text, drawcol);
                }
 
                bbox.ymax -= data->lineh * field->geom.lines;
@@ -284,9 +286,6 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *ar)
 
        BLF_disable(data->fstyle.uifont_id, BLF_WORD_WRAP);
        BLF_disable(blf_mono_font, BLF_WORD_WRAP);
-
-       if (multisample_enabled)
-               glEnable(GL_MULTISAMPLE);
 }
 
 static void ui_tooltip_region_free_cb(ARegion *ar)
@@ -313,6 +312,296 @@ static void ui_tooltip_region_free_cb(ARegion *ar)
 /** \name ToolTip Creation
  * \{ */
 
+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];
+
+       for (wmKeyMapItem *kmi = keymap->items.first; kmi; kmi = kmi->next) {
+               wmOperatorType *ot = WM_operatortype_find(kmi->idname, true);
+               if (ot != NULL) {
+                       /* Tip */
+                       {
+                               uiTooltipField *field = text_field_add(
+                                       data, &(uiTooltipFormat){
+                                           .style = UI_TIP_STYLE_NORMAL,
+                                           .color_id = UI_TIP_LC_MAIN,
+                                           .is_pad = true,
+                                       });
+                               field->text = BLI_strdup(ot->description[0] ? ot->description : ot->name);
+                       }
+                       /* Shortcut */
+                       {
+                               uiTooltipField *field = text_field_add(
+                                       data, &(uiTooltipFormat){
+                                           .style = UI_TIP_STYLE_NORMAL,
+                                           .color_id = UI_TIP_LC_NORMAL,
+                                       });
+                               bool found = false;
+                               if (WM_keymap_item_to_string(kmi, false, buf, sizeof(buf))) {
+                                       found = true;
+                               }
+                               field->text = BLI_sprintfN(TIP_("Shortcut: %s"), found ? buf : "None");
+                       }
+
+                       /* Python */
+                       if (U.flag & USER_TOOLTIPS_PYTHON) {
+                               uiTooltipField *field = text_field_add(
+                                       data, &(uiTooltipFormat){
+                                           .style = UI_TIP_STYLE_NORMAL,
+                                           .color_id = UI_TIP_LC_PYTHON,
+                                       });
+                               char *str = WM_operator_pystring_ex(C, NULL, false, false, ot, kmi->ptr);
+                               WM_operator_pystring_abbreviate(str, 32);
+                               field->text = BLI_sprintfN(TIP_("Python: %s"), str);
+                               MEM_freeN(str);
+                       }
+               }
+       }
+
+       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.
+        */
+
+       /* Title (when icon-only). */
+       if (but->drawstr[0] == '\0') {
+               uiTooltipField *field = text_field_add(
+                       data, &(uiTooltipFormat){
+                           .style = UI_TIP_STYLE_NORMAL,
+                           .color_id = UI_TIP_LC_MAIN,
+                           .is_pad = true,
+                       });
+               field->text = BLI_strdup(tool_name);
+       }
+
+       /* Tip. */
+       {
+               const char *expr_imports[] = {"bpy", "bl_ui", NULL};
+               char expr[256];
+               SNPRINTF(
+                       expr,
+                       "bl_ui.space_toolsystem_common.description_from_name("
+                       "bpy.context, "
+                       "bpy.context.space_data.type, "
+                       "'%s') + '.'",
+                       tool_name);
+
+               char *expr_result = NULL;
+               if (BPY_execute_string_as_string(C, expr_imports, 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);
+                       }
+               }
+               else {
+                       BLI_assert(0);
+               }
+       }
+
+       /* Shortcut. */
+       {
+               /* There are different kinds of shortcuts:
+                *
+                * - Direct access to the tool (as if the toolbar button is pressed).
+                * - The key is bound to a brush type (not the exact brush name).
+                * - The key is assigned to the operator it's self (bypassing the tool, executing the operator).
+                *
+                * Either way case it's useful to show the shortcut.
+                */
+               char *shortcut = NULL;
+
+               {
+                       uiStringInfo op_keymap = {BUT_GET_OP_KEYMAP, NULL};
+                       UI_but_string_info_get(C, but, &op_keymap, NULL);
+                       shortcut = op_keymap.strinfo;
+               }
+
+               if (shortcut == NULL) {
+                       int mode = CTX_data_mode_enum(C);
+                       const char *tool_attr = NULL;
+                       uint tool_offset = 0;
+
+                       switch (mode) {
+                               case CTX_MODE_SCULPT:
+                                       tool_attr = "sculpt_tool";
+                                       tool_offset = offsetof(Brush, sculpt_tool);
+                                       break;
+                               case CTX_MODE_PAINT_VERTEX:
+                                       tool_attr = "vertex_paint_tool";
+                                       tool_offset = offsetof(Brush, vertexpaint_tool);
+                                       break;
+                               case CTX_MODE_PAINT_WEIGHT:
+                                       tool_attr = "weight_paint_tool";
+                                       tool_offset = offsetof(Brush, vertexpaint_tool);
+                                       break;
+                               case CTX_MODE_PAINT_TEXTURE:
+                                       tool_attr = "texture_paint_tool";
+                                       tool_offset = offsetof(Brush, imagepaint_tool);
+                                       break;
+                               default:
+                                       break;
+                       }
+
+                       if (tool_attr != NULL) {
+                               struct Main *bmain = CTX_data_main(C);
+                               Brush *brush = (Brush *)BKE_libblock_find_name(bmain, ID_BR, tool_name);
+                               if (brush) {
+                                       Object *ob = CTX_data_active_object(C);
+                                       wmOperatorType *ot = WM_operatortype_find("paint.brush_select", true);
+
+                                       PointerRNA op_props;
+                                       WM_operator_properties_create_ptr(&op_props, ot);
+                                       RNA_enum_set(&op_props, "paint_mode", ob->mode);
+                                       RNA_enum_set(&op_props, tool_attr, *(((char *)brush) + tool_offset));
+
+                                       /* Check for direct access to the tool. */
+                                       char shortcut_brush[128] = "";
+                                       if (WM_key_event_operator_string(
+                                                   C, ot->idname, WM_OP_INVOKE_REGION_WIN, op_props.data, true,
+                                                   shortcut_brush, ARRAY_SIZE(shortcut_brush)))
+                                       {
+                                               shortcut = BLI_strdup(shortcut_brush);
+                                       }
+                                       WM_operator_properties_free(&op_props);
+                               }
+                       }
+               }
+
+               if (shortcut == NULL) {
+                       /* Check for direct access to the tool. */
+                       char shortcut_toolbar[128] = "";
+                       if (WM_key_event_operator_string(
+                                   C, "WM_OT_toolbar", WM_OP_INVOKE_REGION_WIN, NULL, true,
+                                   shortcut_toolbar, ARRAY_SIZE(shortcut_toolbar)))
+                       {
+                               /* Generate keymap in order to inspect it.
+                                * Note, we could make a utility to avoid the keymap generation part of this. */
+                               const char *expr_imports[] = {"bpy", "bl_ui", NULL};
+                               const char *expr = (
+                                       "getattr("
+                                       "bl_ui.space_toolsystem_common.keymap_from_context("
+                                       "bpy.context, "
+                                       "bpy.context.space_data.type), "
+                                       "'as_pointer', lambda: 0)()");
+
+                               intptr_t expr_result = 0;
+                               if (BPY_execute_string_as_intptr(C, expr_imports, expr, true, &expr_result)) {
+                                       if (expr_result != 0) {
+                                               wmKeyMap *keymap = (wmKeyMap *)expr_result;
+                                               for (wmKeyMapItem *kmi = keymap->items.first; kmi; kmi = kmi->next) {
+                                                       if (STREQ(kmi->idname, but->optype->idname)) {
+                                                               char tool_name_test[MAX_NAME];
+                                                               RNA_string_get(kmi->ptr, "name", tool_name_test);
+                                                               if (STREQ(tool_name, tool_name_test)) {
+                                                                       char buf[128];
+                                                                       WM_keymap_item_to_string(kmi, false, buf, sizeof(buf));
+                                                                       shortcut = BLI_sprintfN("%s, %s", shortcut_toolbar, buf);
+                                                                       break;
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               }
+                               else {
+                                       BLI_assert(0);
+                               }
+                       }
+               }
+
+               if (shortcut != NULL) {
+                       uiTooltipField *field = text_field_add(
+                               data, &(uiTooltipFormat){
+                                   .style = UI_TIP_STYLE_NORMAL,
+                                   .color_id = UI_TIP_LC_VALUE,
+                                   .is_pad = true,
+                               });
+                       field->text = BLI_sprintfN(TIP_("Shortcut: %s"), shortcut);
+                       MEM_freeN(shortcut);
+               }
+       }
+
+       /* Keymap */
+
+       /* This is too handy not to expose somehow, let's be sneaky for now. */
+       if (CTX_wm_window(C)->eventstate->shift) {
+               const char *expr_imports[] = {"bpy", "bl_ui", NULL};
+               char expr[256];
+               SNPRINTF(
+                       expr,
+                       "getattr("
+                       "bl_ui.space_toolsystem_common.keymap_from_name("
+                       "bpy.context, "
+                       "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_imports, expr, true, &expr_result)) {
+                       if (expr_result != 0) {
+                               {
+                                       uiTooltipField *field = text_field_add(
+                                               data, &(uiTooltipFormat){
+                                                   .style = UI_TIP_STYLE_NORMAL,
+                                                   .color_id = UI_TIP_LC_NORMAL,
+                                                   .is_pad = true,
+                                               });
+                                       field->text = BLI_strdup("Tool Keymap:");
+                               }
+                               wmKeyMap *keymap = (wmKeyMap *)expr_result;
+                               ui_tooltip_data_append_from_keymap(C, data, keymap);
+                       }
+               }
+               else {
+                       BLI_assert(0);
+               }
+       }
+#endif  /* WITH_PYTHON */
+
+       if (data->fields_len == 0) {
+               MEM_freeN(data);
+               return NULL;
+       }
+       else {
+               return data;
+       }
+}
+
 static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but)
 {
        uiStringInfo but_tip = {BUT_GET_TIP, NULL};
@@ -463,7 +752,7 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but)
                WM_operator_pystring_abbreviate(str, 32);
 
                /* operator info */
-               if ((U.flag & USER_TOOLTIPS_PYTHON) == 0) {
+               if (U.flag & USER_TOOLTIPS_PYTHON) {
                        uiTooltipField *field = text_field_add(
                                data, &(uiTooltipFormat){
                                    .style = UI_TIP_STYLE_MONO,
@@ -501,7 +790,7 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but)
                }
        }
 
-       if ((U.flag & USER_TOOLTIPS_PYTHON) == 0 && !but->optype && rna_struct.strinfo) {
+       if ((U.flag & USER_TOOLTIPS_PYTHON) && !but->optype && rna_struct.strinfo) {
                {
                        uiTooltipField *field = text_field_add(
                                data, &(uiTooltipFormat){
@@ -565,6 +854,112 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but)
        }
 }
 
+static uiTooltipData *ui_tooltip_data_from_gizmo(bContext *C, wmGizmo *gz)
+{
+       uiTooltipData *data = MEM_callocN(sizeof(uiTooltipData), "uiTooltipData");
+
+       /* TODO(campbell): a way for gizmos to have their own descriptions (low priority). */
+
+       /* Operator Actions */
+       {
+               bool use_drag = gz->drag_part != -1 && gz->highlight_part != gz->drag_part;
+
+               const struct {
+                       int part;
+                       const char *prefix;
+               } mpop_actions[] = {
+                       {
+                               .part = gz->highlight_part,
+                               .prefix = use_drag ? TIP_("Click") : NULL,
+                       }, {
+                               .part = use_drag ? gz->drag_part : -1,
+                               .prefix = use_drag ? TIP_("Drag") : NULL,
+                       },
+               };
+
+               for (int i = 0; i < ARRAY_SIZE(mpop_actions); i++) {
+                       wmGizmoOpElem *mpop = (mpop_actions[i].part != -1) ? WM_gizmo_operator_get(gz, mpop_actions[i].part) : NULL;
+                       if (mpop != NULL) {
+                               /* Description */
+                               const char *info = RNA_struct_ui_description(mpop->type->srna);
+                               if (!(info && info[0])) {
+                                       info  = RNA_struct_ui_name(mpop->type->srna);
+                               }
+
+                               if (info && info[0]) {
+                                       char *text = NULL;
+                                       if (mpop_actions[i].prefix != NULL) {
+                                               text = BLI_sprintfN("%s: %s", mpop_actions[i].prefix, info);
+                                       }
+                                       else {
+                                               text = BLI_strdup(info);
+                                       }
+
+                                       if (text != NULL) {
+                                               uiTooltipField *field = text_field_add(
+                                                       data, &(uiTooltipFormat){
+                                                           .style = UI_TIP_STYLE_HEADER,
+                                                           .color_id = UI_TIP_LC_VALUE,
+                                                           .is_pad = true,
+                                                       });
+                                               field->text = text;
+                                       }
+                               }
+
+                               /* Shortcut */
+                               {
+                                       bool found = false;
+                                       IDProperty *prop = mpop->ptr.data;
+                                       char buf[128];
+                                       if (WM_key_event_operator_string(
+                                                   C, mpop->type->idname, WM_OP_INVOKE_DEFAULT, prop, true,
+                                                   buf, ARRAY_SIZE(buf)))
+                                       {
+                                               found = true;
+                                       }
+                                       uiTooltipField *field = text_field_add(
+                                               data, &(uiTooltipFormat){
+                                                   .style = UI_TIP_STYLE_NORMAL,
+                                                   .color_id = UI_TIP_LC_VALUE,
+                                                   .is_pad = true,
+                                               });
+                                       field->text = BLI_sprintfN(TIP_("Shortcut: %s"), found ? buf : "None");
+                               }
+                       }
+               }
+       }
+
+       /* Property Actions */
+       if (gz->type->target_property_defs_len) {
+               wmGizmoProperty *gz_prop_array = WM_gizmo_target_property_array(gz);
+               for (int i = 0; i < gz->type->target_property_defs_len; i++) {
+                       /* TODO(campbell): function callback descriptions. */
+                       wmGizmoProperty *gz_prop = &gz_prop_array[i];
+                       if (gz_prop->prop != NULL) {
+                               const char *info = RNA_property_ui_description(gz_prop->prop);
+                               if (info && info[0]) {
+                                       uiTooltipField *field = text_field_add(
+                                               data, &(uiTooltipFormat){
+                                                   .style = UI_TIP_STYLE_NORMAL,
+                                                   .color_id = UI_TIP_LC_VALUE,
+                                                   .is_pad = true,
+                                               });
+                                       field->text = BLI_strdup(info);
+                               }
+                       }
+               }
+       }
+
+       if (data->fields_len == 0) {
+               MEM_freeN(data);
+               return NULL;
+       }
+       else {
+               return data;
+       }
+}
+
+
 static ARegion *ui_tooltip_create_with_data(
         bContext *C, uiTooltipData *data,
         const float init_position[2],
@@ -708,7 +1103,7 @@ static ARegion *ui_tooltip_create_with_data(
        }
 
        /* adds subwindow */
-       ED_region_init(C, ar);
+       ED_region_init(ar);
 
        /* notify change and redraw */
        ED_region_tag_redraw(ar);
@@ -735,9 +1130,14 @@ ARegion *UI_tooltip_create_from_button(bContext *C, ARegion *butregion, uiBut *b
        }
        uiTooltipData *data = NULL;
 
+       if (data == NULL) {
+               data = ui_tooltip_data_from_tool(C, but);
+       }
+
        if (data == NULL) {
                data = ui_tooltip_data_from_button(C, but);
        }
+
        if (data == NULL) {
                return NULL;
        }
@@ -753,6 +1153,23 @@ ARegion *UI_tooltip_create_from_button(bContext *C, ARegion *butregion, uiBut *b
        return ui_tooltip_create_with_data(C, data, init_position, aspect);
 }
 
+ARegion *UI_tooltip_create_from_gizmo(bContext *C, wmGizmo *gz)
+{
+       wmWindow *win = CTX_wm_window(C);
+       const float aspect = 1.0f;
+       float init_position[2];
+
+       uiTooltipData *data = ui_tooltip_data_from_gizmo(C, gz);
+       if (data == NULL) {
+               return NULL;
+       }
+
+       init_position[0] = win->eventstate->x;
+       init_position[1] = win->eventstate->y;
+
+       return ui_tooltip_create_with_data(C, data, init_position, aspect);
+}
+
 void UI_tooltip_free(bContext *C, bScreen *sc, ARegion *ar)
 {
        ui_region_temp_remove(C, sc, ar);