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:
authorCampbell Barton <ideasman42@gmail.com>2017-11-03 13:22:27 +0300
committerCampbell Barton <ideasman42@gmail.com>2017-11-03 13:22:27 +0300
commit3a7a7ed03b7611a29de0e2a27e358035aee83dce (patch)
tree24573d439cc204217a038ee07616fb75477f358a /source/blender/editors/interface/interface_region_tooltip.c
parentcd4f925f564a01d96038c83a9d39396e275f71a4 (diff)
parent2a87bd89951204a5dd15b60ba86cbba72fc3b088 (diff)
Merge branch 'master' into blender2.8
Diffstat (limited to 'source/blender/editors/interface/interface_region_tooltip.c')
-rw-r--r--source/blender/editors/interface/interface_region_tooltip.c689
1 files changed, 689 insertions, 0 deletions
diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c
new file mode 100644
index 00000000000..601a2d2e11a
--- /dev/null
+++ b/source/blender/editors/interface/interface_region_tooltip.c
@@ -0,0 +1,689 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2008 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/interface/interface_region_tooltip.c
+ * \ingroup edinterface
+ *
+ * ToolTip Region and Construction
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_userdef_types.h"
+
+#include "BLI_math.h"
+#include "BLI_string.h"
+#include "BLI_string_utf8.h"
+#include "BLI_rect.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_context.h"
+#include "BKE_screen.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "RNA_access.h"
+
+#include "BIF_gl.h"
+
+#include "UI_interface.h"
+
+#include "BLF_api.h"
+#include "BLT_translation.h"
+
+#include "ED_screen.h"
+
+#include "interface_intern.h"
+#include "interface_regions_intern.h"
+
+#define UI_TIP_PAD_FAC 1.3f
+#define UI_TIP_PADDING (int)(UI_TIP_PAD_FAC * UI_UNIT_Y)
+#define UI_TIP_MAXWIDTH 600
+
+#define MAX_TOOLTIP_LINES 8
+typedef struct uiTooltipData {
+ rcti bbox;
+ uiFontStyle fstyle;
+ char lines[MAX_TOOLTIP_LINES][2048];
+ char header[2048], active_info[2048];
+ struct {
+ enum {
+ UI_TIP_STYLE_NORMAL = 0,
+ UI_TIP_STYLE_HEADER,
+ UI_TIP_STYLE_MONO,
+ } style : 3;
+ enum {
+ UI_TIP_LC_MAIN = 0, /* primary text */
+ UI_TIP_LC_VALUE, /* the value of buttons (also shortcuts) */
+ UI_TIP_LC_ACTIVE, /* titles of active enum values */
+ UI_TIP_LC_NORMAL, /* regular text */
+ UI_TIP_LC_PYTHON, /* Python snippet */
+ UI_TIP_LC_ALERT, /* description of why operator can't run */
+ } color_id : 4;
+ int is_pad : 1;
+ } format[MAX_TOOLTIP_LINES];
+
+ struct {
+ uint x_pos; /* x cursor position at the end of the last line */
+ uint lines; /* number of lines, 1 or more with word-wrap */
+ } line_geom[MAX_TOOLTIP_LINES];
+
+ int wrap_width;
+
+ int totline;
+ int toth, lineh;
+} uiTooltipData;
+
+#define UI_TIP_LC_MAX 6
+
+BLI_STATIC_ASSERT(UI_TIP_LC_MAX == UI_TIP_LC_ALERT + 1, "invalid lc-max");
+BLI_STATIC_ASSERT(sizeof(((uiTooltipData *)NULL)->format[0]) <= sizeof(int), "oversize");
+
+/* -------------------------------------------------------------------- */
+/** \name ToolTip Callbacks (Draw & Free)
+ * \{ */
+
+static void rgb_tint(
+ float col[3],
+ float h, float h_strength,
+ float v, float v_strength)
+{
+ float col_hsv_from[3];
+ float col_hsv_to[3];
+
+ rgb_to_hsv_v(col, col_hsv_from);
+
+ col_hsv_to[0] = h;
+ col_hsv_to[1] = h_strength;
+ col_hsv_to[2] = (col_hsv_from[2] * (1.0f - v_strength)) + (v * v_strength);
+
+ hsv_to_rgb_v(col_hsv_to, col);
+}
+
+static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *ar)
+{
+ const float pad_px = UI_TIP_PADDING;
+ uiTooltipData *data = ar->regiondata;
+ 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];
+ float *active_color = tip_colors[UI_TIP_LC_ACTIVE];
+ float *normal_color = tip_colors[UI_TIP_LC_NORMAL];
+ float *python_color = tip_colors[UI_TIP_LC_PYTHON];
+ float *alert_color = tip_colors[UI_TIP_LC_ALERT];
+
+ 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);
+
+ wmOrtho2_region_pixelspace(ar);
+
+ /* draw background */
+ ui_draw_tooltip_background(UI_style_get(), NULL, &bbox);
+
+ /* set background_color */
+ rgb_uchar_to_float(background_color, (const unsigned char *)theme->inner);
+
+ /* calculate normal_color */
+ rgb_uchar_to_float(main_color, (const unsigned char *)theme->text);
+ copy_v3_v3(active_color, main_color);
+ copy_v3_v3(normal_color, main_color);
+ copy_v3_v3(python_color, main_color);
+ copy_v3_v3(alert_color, main_color);
+ copy_v3_v3(value_color, main_color);
+
+ /* find the brightness difference between background and text colors */
+
+ tone_bg = rgb_to_grayscale(background_color);
+ /* tone_fg = rgb_to_grayscale(main_color); */
+
+ /* mix the colors */
+ rgb_tint(value_color, 0.0f, 0.0f, tone_bg, 0.2f); /* light gray */
+ rgb_tint(active_color, 0.6f, 0.2f, tone_bg, 0.2f); /* light blue */
+ rgb_tint(normal_color, 0.0f, 0.0f, tone_bg, 0.4f); /* gray */
+ rgb_tint(python_color, 0.0f, 0.0f, tone_bg, 0.5f); /* dark gray */
+ rgb_tint(alert_color, 0.0f, 0.8f, tone_bg, 0.1f); /* red */
+
+ /* draw text */
+ BLF_wordwrap(data->fstyle.uifont_id, data->wrap_width);
+ BLF_wordwrap(blf_mono_font, data->wrap_width);
+
+ bbox.xmin += 0.5f * pad_px; /* add padding to the text */
+ bbox.ymax -= 0.25f * pad_px;
+
+ for (i = 0; i < data->totline; i++) {
+ bbox.ymin = bbox.ymax - (data->lineh * data->line_geom[i].lines);
+ if (data->format[i].style == UI_TIP_STYLE_HEADER) {
+ /* draw header and active data (is done here to be able to change color) */
+ uiFontStyle fstyle_header = data->fstyle;
+ float xofs, yofs;
+
+ /* override text-style */
+ fstyle_header.shadow = 1;
+ fstyle_header.shadowcolor = rgb_to_grayscale(tip_colors[UI_TIP_LC_MAIN]);
+ fstyle_header.shadx = fstyle_header.shady = 0;
+ 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);
+ UI_fontstyle_draw(&fstyle_header, &bbox, data->header, drawcol);
+
+ /* offset to the end of the last line */
+ xofs = data->line_geom[i].x_pos;
+ yofs = data->lineh * (data->line_geom[i].lines - 1);
+ bbox.xmin += xofs;
+ bbox.ymax -= yofs;
+
+ fstyle_header.shadow = 0;
+ rgb_float_to_uchar(drawcol, tip_colors[UI_TIP_LC_ACTIVE]);
+ UI_fontstyle_draw(&fstyle_header, &bbox, data->active_info, drawcol);
+
+ /* undo offset */
+ bbox.xmin -= xofs;
+ bbox.ymax += yofs;
+ }
+ else if (data->format[i].style == UI_TIP_STYLE_MONO) {
+ uiFontStyle fstyle_mono = data->fstyle;
+ fstyle_mono.uifont_id = blf_mono_font;
+ fstyle_mono.word_wrap = true;
+
+ 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);
+ rgb_float_to_uchar(drawcol, tip_colors[data->format[i].color_id]);
+ UI_fontstyle_draw(&fstyle_mono, &bbox, data->lines[i], drawcol);
+ }
+ else {
+ uiFontStyle fstyle_normal = data->fstyle;
+ BLI_assert(data->format[i].style == UI_TIP_STYLE_NORMAL);
+ fstyle_normal.word_wrap = true;
+
+ /* draw remaining data */
+ rgb_float_to_uchar(drawcol, tip_colors[data->format[i].color_id]);
+ UI_fontstyle_set(&fstyle_normal);
+ UI_fontstyle_draw(&fstyle_normal, &bbox, data->lines[i], drawcol);
+ }
+
+ bbox.ymax -= data->lineh * data->line_geom[i].lines;
+
+ if ((i + 1 != data->totline) && data->format[i + 1].is_pad) {
+ bbox.ymax -= data->lineh * (UI_TIP_PAD_FAC - 1);
+ }
+ }
+
+ 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)
+{
+ uiTooltipData *data;
+
+ data = ar->regiondata;
+ MEM_freeN(data);
+ ar->regiondata = NULL;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name ToolTip Creation
+ * \{ */
+
+static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but)
+{
+ uiStringInfo but_tip = {BUT_GET_TIP, NULL};
+ uiStringInfo enum_label = {BUT_GET_RNAENUM_LABEL, NULL};
+ uiStringInfo enum_tip = {BUT_GET_RNAENUM_TIP, NULL};
+ uiStringInfo op_keymap = {BUT_GET_OP_KEYMAP, NULL};
+ uiStringInfo prop_keymap = {BUT_GET_PROP_KEYMAP, NULL};
+ uiStringInfo rna_struct = {BUT_GET_RNASTRUCT_IDENTIFIER, NULL};
+ uiStringInfo rna_prop = {BUT_GET_RNAPROP_IDENTIFIER, NULL};
+
+ char buf[512];
+
+ /* create tooltip data */
+ uiTooltipData *data = MEM_callocN(sizeof(uiTooltipData), "uiTooltipData");
+
+ UI_but_string_info_get(C, but, &but_tip, &enum_label, &enum_tip, &op_keymap, &prop_keymap, &rna_struct, &rna_prop, NULL);
+
+ /* Tip */
+ if (but_tip.strinfo) {
+ if (enum_label.strinfo) {
+ BLI_snprintf(data->header, sizeof(data->header), "%s: ", but_tip.strinfo);
+ BLI_strncpy(data->active_info, enum_label.strinfo, sizeof(data->lines[0]));
+ }
+ else {
+ BLI_snprintf(data->header, sizeof(data->header), "%s.", but_tip.strinfo);
+ }
+ data->format[data->totline].style = UI_TIP_STYLE_HEADER;
+ data->totline++;
+
+ /* special case enum rna buttons */
+ if ((but->type & UI_BTYPE_ROW) && but->rnaprop && RNA_property_flag(but->rnaprop) & PROP_ENUM_FLAG) {
+ BLI_strncpy(data->lines[data->totline], IFACE_("(Shift-Click/Drag to select multiple)"),
+ sizeof(data->lines[0]));
+
+ data->format[data->totline].color_id = UI_TIP_LC_NORMAL;
+ data->totline++;
+ }
+
+ }
+ /* Enum item label & tip */
+ if (enum_tip.strinfo) {
+ BLI_strncpy(data->lines[data->totline], enum_tip.strinfo, sizeof(data->lines[0]));
+ data->format[data->totline].is_pad = true;
+ data->format[data->totline].color_id = UI_TIP_LC_VALUE;
+ data->totline++;
+ }
+
+ /* Op shortcut */
+ if (op_keymap.strinfo) {
+ BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Shortcut: %s"), op_keymap.strinfo);
+ data->format[data->totline].is_pad = true;
+ data->format[data->totline].color_id = UI_TIP_LC_VALUE;
+ data->totline++;
+ }
+
+ /* Property context-toggle shortcut */
+ if (prop_keymap.strinfo) {
+ BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Shortcut: %s"), prop_keymap.strinfo);
+ data->format[data->totline].is_pad = true;
+ data->format[data->totline].color_id = UI_TIP_LC_VALUE;
+ data->totline++;
+ }
+
+ if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) {
+ /* better not show the value of a password */
+ if ((but->rnaprop && (RNA_property_subtype(but->rnaprop) == PROP_PASSWORD)) == 0) {
+ /* full string */
+ ui_but_string_get(but, buf, sizeof(buf));
+ if (buf[0]) {
+ BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Value: %s"), buf);
+ data->format[data->totline].is_pad = true;
+ data->format[data->totline].color_id = UI_TIP_LC_VALUE;
+ data->totline++;
+ }
+ }
+ }
+
+ if (but->rnaprop) {
+ int unit_type = UI_but_unit_type_get(but);
+
+ if (unit_type == PROP_UNIT_ROTATION) {
+ if (RNA_property_type(but->rnaprop) == PROP_FLOAT) {
+ float value = RNA_property_array_check(but->rnaprop) ?
+ RNA_property_float_get_index(&but->rnapoin, but->rnaprop, but->rnaindex) :
+ RNA_property_float_get(&but->rnapoin, but->rnaprop);
+ BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Radians: %f"), value);
+ data->format[data->totline].color_id = UI_TIP_LC_NORMAL;
+ data->totline++;
+ }
+ }
+
+ if (but->flag & UI_BUT_DRIVEN) {
+ if (ui_but_anim_expression_get(but, buf, sizeof(buf))) {
+ /* expression */
+ BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Expression: %s"), buf);
+ data->format[data->totline].color_id = UI_TIP_LC_NORMAL;
+ data->totline++;
+ }
+ }
+
+ if (but->rnapoin.id.data) {
+ ID *id = but->rnapoin.id.data;
+ if (ID_IS_LINKED_DATABLOCK(id)) {
+ BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Library: %s"), id->lib->name);
+ data->format[data->totline].color_id = UI_TIP_LC_NORMAL;
+ data->totline++;
+ }
+ }
+ }
+ else if (but->optype) {
+ PointerRNA *opptr;
+ char *str;
+ opptr = UI_but_operator_ptr_get(but); /* allocated when needed, the button owns it */
+
+ /* so the context is passed to itemf functions (some py itemf functions use it) */
+ WM_operator_properties_sanitize(opptr, false);
+
+ str = WM_operator_pystring_ex(C, NULL, false, false, but->optype, opptr);
+
+ /* avoid overly verbose tips (eg, arrays of 20 layers), exact limit is arbitrary */
+ WM_operator_pystring_abbreviate(str, 32);
+
+ /* operator info */
+ if ((U.flag & USER_TOOLTIPS_PYTHON) == 0) {
+ BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Python: %s"), str);
+ data->format[data->totline].style = UI_TIP_STYLE_MONO;
+ data->format[data->totline].is_pad = true;
+ data->format[data->totline].color_id = UI_TIP_LC_PYTHON;
+ data->totline++;
+ }
+
+ MEM_freeN(str);
+ }
+
+ /* button is disabled, we may be able to tell user why */
+ if (but->flag & UI_BUT_DISABLED) {
+ const char *disabled_msg = NULL;
+
+ /* if operator poll check failed, it can give pretty precise info why */
+ if (but->optype) {
+ CTX_wm_operator_poll_msg_set(C, NULL);
+ WM_operator_poll_context(C, but->optype, but->opcontext);
+ disabled_msg = CTX_wm_operator_poll_msg_get(C);
+ }
+ /* alternatively, buttons can store some reasoning too */
+ else if (but->disabled_info) {
+ disabled_msg = TIP_(but->disabled_info);
+ }
+
+ if (disabled_msg && disabled_msg[0]) {
+ BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Disabled: %s"), disabled_msg);
+ data->format[data->totline].color_id = UI_TIP_LC_ALERT;
+ data->totline++;
+ }
+ }
+
+ if ((U.flag & USER_TOOLTIPS_PYTHON) == 0 && !but->optype && rna_struct.strinfo) {
+ if (rna_prop.strinfo) {
+ /* Struct and prop */
+ BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]),
+ TIP_("Python: %s.%s"),
+ rna_struct.strinfo, rna_prop.strinfo);
+ }
+ else {
+ /* Only struct (e.g. menus) */
+ BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]),
+ TIP_("Python: %s"), rna_struct.strinfo);
+ }
+ data->format[data->totline].style = UI_TIP_STYLE_MONO;
+ data->format[data->totline].is_pad = true;
+ data->format[data->totline].color_id = UI_TIP_LC_PYTHON;
+ data->totline++;
+
+ if (but->rnapoin.id.data) {
+ /* this could get its own 'BUT_GET_...' type */
+
+ /* never fails */
+ char *id_path;
+
+ if (but->rnaprop) {
+ id_path = RNA_path_full_property_py_ex(&but->rnapoin, but->rnaprop, but->rnaindex, true);
+ }
+ else {
+ id_path = RNA_path_full_struct_py(&but->rnapoin);
+ }
+
+ BLI_strncat_utf8(data->lines[data->totline], id_path, sizeof(data->lines[0]));
+ MEM_freeN(id_path);
+
+ data->format[data->totline].style = UI_TIP_STYLE_MONO;
+ data->format[data->totline].color_id = UI_TIP_LC_PYTHON;
+ data->totline++;
+ }
+ }
+
+ /* Free strinfo's... */
+ if (but_tip.strinfo)
+ MEM_freeN(but_tip.strinfo);
+ if (enum_label.strinfo)
+ MEM_freeN(enum_label.strinfo);
+ if (enum_tip.strinfo)
+ MEM_freeN(enum_tip.strinfo);
+ if (op_keymap.strinfo)
+ MEM_freeN(op_keymap.strinfo);
+ if (prop_keymap.strinfo)
+ MEM_freeN(prop_keymap.strinfo);
+ if (rna_struct.strinfo)
+ MEM_freeN(rna_struct.strinfo);
+ if (rna_prop.strinfo)
+ MEM_freeN(rna_prop.strinfo);
+
+ BLI_assert(data->totline < MAX_TOOLTIP_LINES);
+
+ if (data->totline == 0) {
+ MEM_freeN(data);
+ return NULL;
+ }
+ else {
+ return data;
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name ToolTip Public API
+ * \{ */
+
+ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but)
+{
+ const float pad_px = UI_TIP_PADDING;
+ wmWindow *win = CTX_wm_window(C);
+ const int winx = WM_window_pixels_x(win);
+ uiStyle *style = UI_style_get();
+ static ARegionType type;
+ ARegion *ar;
+/* IDProperty *prop;*/
+ /* aspect values that shrink text are likely unreadable */
+ const float aspect = min_ff(1.0f, but->block->aspect);
+ int fonth, fontw;
+ int ofsx, ofsy, h, i;
+ rctf rect_fl;
+ rcti rect_i;
+ int font_flag = 0;
+
+ if (but->drawflag & UI_BUT_NO_TOOLTIP) {
+ return NULL;
+ }
+
+ uiTooltipData *data = ui_tooltip_data_from_button(C, but);
+ if (data == NULL) {
+ return NULL;
+ }
+
+ /* create area region */
+ ar = ui_region_temp_add(CTX_wm_screen(C));
+
+ memset(&type, 0, sizeof(ARegionType));
+ type.draw = ui_tooltip_region_draw_cb;
+ type.free = ui_tooltip_region_free_cb;
+ type.regionid = RGN_TYPE_TEMPORARY;
+ ar->type = &type;
+
+ /* set font, get bb */
+ data->fstyle = style->widget; /* copy struct */
+ ui_fontscale(&data->fstyle.points, aspect);
+
+ UI_fontstyle_set(&data->fstyle);
+
+ data->wrap_width = min_ii(UI_TIP_MAXWIDTH * U.pixelsize / aspect, winx - (UI_TIP_PADDING * 2));
+
+ font_flag |= BLF_WORD_WRAP;
+ if (data->fstyle.kerning == 1) {
+ font_flag |= BLF_KERNING_DEFAULT;
+ }
+ BLF_enable(data->fstyle.uifont_id, font_flag);
+ BLF_enable(blf_mono_font, font_flag);
+ BLF_wordwrap(data->fstyle.uifont_id, data->wrap_width);
+ BLF_wordwrap(blf_mono_font, data->wrap_width);
+
+ /* these defines tweaked depending on font */
+#define TIP_BORDER_X (16.0f / aspect)
+#define TIP_BORDER_Y (6.0f / aspect)
+
+ h = BLF_height_max(data->fstyle.uifont_id);
+
+ for (i = 0, fontw = 0, fonth = 0; i < data->totline; i++) {
+ struct ResultBLF info;
+ int w, x_pos = 0;
+
+ if (data->format[i].style == UI_TIP_STYLE_HEADER) {
+ w = BLF_width_ex(data->fstyle.uifont_id, data->header, sizeof(data->header), &info);
+ /* check for enum label */
+ if (data->active_info[0]) {
+ x_pos = info.width;
+ w = max_ii(w, x_pos + BLF_width(data->fstyle.uifont_id, data->active_info, sizeof(data->active_info)));
+ }
+ }
+ else if (data->format[i].style == UI_TIP_STYLE_MONO) {
+ BLF_size(blf_mono_font, data->fstyle.points * U.pixelsize, U.dpi);
+
+ w = BLF_width_ex(blf_mono_font, data->lines[i], sizeof(data->lines[i]), &info);
+ }
+ else {
+ BLI_assert(data->format[i].style == UI_TIP_STYLE_NORMAL);
+
+ w = BLF_width_ex(data->fstyle.uifont_id, data->lines[i], sizeof(data->lines[i]), &info);
+ }
+
+ fontw = max_ii(fontw, w);
+
+ fonth += h * info.lines;
+ if ((i + 1 != data->totline) && data->format[i + 1].is_pad) {
+ fonth += h * (UI_TIP_PAD_FAC - 1);
+ }
+
+ data->line_geom[i].lines = info.lines;
+ data->line_geom[i].x_pos = x_pos;
+ }
+
+ //fontw *= aspect;
+
+ BLF_disable(data->fstyle.uifont_id, font_flag);
+ BLF_disable(blf_mono_font, font_flag);
+
+ ar->regiondata = data;
+
+ data->toth = fonth;
+ data->lineh = h;
+
+ /* compute position */
+ ofsx = 0; //(but->block->panel) ? but->block->panel->ofsx : 0;
+ ofsy = 0; //(but->block->panel) ? but->block->panel->ofsy : 0;
+
+ rect_fl.xmin = BLI_rctf_cent_x(&but->rect) + ofsx - TIP_BORDER_X;
+ rect_fl.xmax = rect_fl.xmin + fontw + pad_px;
+ rect_fl.ymax = but->rect.ymin + ofsy - TIP_BORDER_Y;
+ rect_fl.ymin = rect_fl.ymax - fonth - TIP_BORDER_Y;
+
+ /* since the text has beens caled already, the size of tooltips is defined now */
+ /* here we try to figure out the right location */
+ if (butregion) {
+ float mx, my;
+ float ofsx_fl = rect_fl.xmin, ofsy_fl = rect_fl.ymax;
+ ui_block_to_window_fl(butregion, but->block, &ofsx_fl, &ofsy_fl);
+
+#if 1
+ /* use X mouse location */
+ mx = (win->eventstate->x + (TIP_BORDER_X * 2)) - BLI_rctf_cent_x(&but->rect);
+#else
+ mx = ofsx_fl - rect_fl.xmin;
+#endif
+ my = ofsy_fl - rect_fl.ymax;
+
+ BLI_rctf_translate(&rect_fl, mx, my);
+ }
+ BLI_rcti_rctf_copy(&rect_i, &rect_fl);
+
+#undef TIP_BORDER_X
+#undef TIP_BORDER_Y
+
+ /* clip with window boundaries */
+ if (rect_i.xmax > winx) {
+ /* super size */
+ if (rect_i.xmax > winx + rect_i.xmin) {
+ rect_i.xmax = winx;
+ rect_i.xmin = 0;
+ }
+ else {
+ rect_i.xmin -= rect_i.xmax - winx;
+ rect_i.xmax = winx;
+ }
+ }
+ /* ensure at least 5 px above screen bounds
+ * 25 is just a guess to be above the menu item */
+ if (rect_i.ymin < 5) {
+ rect_i.ymax += (-rect_i.ymin) + 30;
+ rect_i.ymin = 30;
+ }
+
+ /* add padding */
+ BLI_rcti_resize(&rect_i,
+ BLI_rcti_size_x(&rect_i) + pad_px,
+ BLI_rcti_size_y(&rect_i) + pad_px);
+
+ /* widget rect, in region coords */
+ {
+ const int margin = UI_POPUP_MARGIN;
+
+ data->bbox.xmin = margin;
+ data->bbox.xmax = BLI_rcti_size_x(&rect_i) - margin;
+ data->bbox.ymin = margin;
+ data->bbox.ymax = BLI_rcti_size_y(&rect_i);
+
+ /* region bigger for shadow */
+ ar->winrct.xmin = rect_i.xmin - margin;
+ ar->winrct.xmax = rect_i.xmax + margin;
+ ar->winrct.ymin = rect_i.ymin - margin;
+ ar->winrct.ymax = rect_i.ymax + margin;
+ }
+
+ /* adds subwindow */
+ ED_region_init(C, ar);
+
+ /* notify change and redraw */
+ ED_region_tag_redraw(ar);
+
+ return ar;
+}
+
+void ui_tooltip_free(bContext *C, ARegion *ar)
+{
+ ui_region_temp_remove(C, CTX_wm_screen(C), ar);
+}
+
+/** \} */