Blender V4.3
interface_region_tooltip.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2008 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
10
11/* TODO(@ideasman42):
12 * We may want to have a higher level API that initializes a timer,
13 * checks for mouse motion and clears the tool-tip afterwards.
14 * We never want multiple tool-tips at once
15 * so this could be handled on the window / window-manager level.
16 *
17 * For now it's not a priority, so leave as-is.
18 */
19
20#include <algorithm>
21#include <cstdarg>
22#include <cstdlib>
23#include <cstring>
24#include <memory>
25
26#include <fmt/format.h>
27
28#include "MEM_guardedalloc.h"
29
30#include "DNA_userdef_types.h"
31
32#include "BLI_fileops.h"
33#include "BLI_listbase.h"
34#include "BLI_math_color.h"
35#include "BLI_math_vector.h"
36#include "BLI_path_utils.hh"
37#include "BLI_rect.h"
38#include "BLI_string.h"
39#include "BLI_utildefines.h"
40
41#include "BKE_context.hh"
42#include "BKE_idtype.hh"
43#include "BKE_image.hh"
44#include "BKE_paint.hh"
45#include "BKE_screen.hh"
46
47#include "BIF_glutil.hh"
48
49#include "DNA_vfont_types.h"
50
51#include "GPU_immediate.hh"
52#include "GPU_immediate_util.hh"
53#include "GPU_state.hh"
54
55#include "IMB_imbuf.hh"
56#include "IMB_imbuf_types.hh"
57#include "IMB_thumbs.hh"
58
59#include "WM_api.hh"
60#include "WM_types.hh"
61
62#include "RNA_access.hh"
63#include "RNA_path.hh"
64#include "RNA_prototypes.hh"
65
66#include "UI_interface.hh"
67
68#include "BLF_api.hh"
69#include "BLT_translation.hh"
70
71#ifdef WITH_PYTHON
72# include "BPY_extern_run.hh"
73#endif
74
75#include "ED_screen.hh"
76
77#include "interface_intern.hh"
79
80#define UI_TIP_SPACER 0.3f
81#define UI_TIP_PADDING int(1.3f * UI_UNIT_Y)
82#define UI_TIP_MAXWIDTH 600
83#define UI_TIP_MAXIMAGEWIDTH 500
84#define UI_TIP_MAXIMAGEHEIGHT 300
85#define UI_TIP_STR_MAX 1024
86
91
93 std::string text;
94 std::string text_suffix;
95 struct {
102 std::optional<uiTooltipImage> image;
103};
104
112
113BLI_STATIC_ASSERT(int(UI_TIP_LC_MAX) == int(UI_TIP_LC_ALERT) + 1, "invalid lc-max");
114
116 std::string text,
117 std::string suffix,
118 const uiTooltipStyle style,
119 const uiTooltipColorID color_id,
120 const bool is_pad)
121{
122 if (is_pad) {
123 /* Add a spacer field before this one. */
125 }
126
127 uiTooltipField field{};
128 field.format.style = style;
129 field.format.color_id = color_id;
130 field.text = std::move(text);
131 field.text_suffix = std::move(suffix);
132 data.fields.append(std::move(field));
133}
134
136{
137 uiTooltipField field{};
139 field.image = image_data;
140 field.image->ibuf = IMB_dupImBuf(image_data.ibuf);
141 data.fields.append(std::move(field));
142}
143
144/* -------------------------------------------------------------------- */
147
148static void rgb_tint(float col[3], float h, float h_strength, float v, float v_strength)
149{
150 float col_hsv_from[3];
151 float col_hsv_to[3];
152
153 rgb_to_hsv_v(col, col_hsv_from);
154
155 col_hsv_to[0] = h;
156 col_hsv_to[1] = h_strength;
157 col_hsv_to[2] = (col_hsv_from[2] * (1.0f - v_strength)) + (v * v_strength);
158
159 hsv_to_rgb_v(col_hsv_to, col);
160}
161
162static void ui_tooltip_region_draw_cb(const bContext * /*C*/, ARegion *region)
163{
164 const float pad_px = UI_TIP_PADDING;
165 uiTooltipData *data = static_cast<uiTooltipData *>(region->regiondata);
166 const uiWidgetColors *theme = ui_tooltip_get_theme();
167 rcti bbox = data->bbox;
168 float tip_colors[UI_TIP_LC_MAX][3];
169 uchar drawcol[4] = {0, 0, 0, 255}; /* to store color in while drawing (alpha is always 255) */
170
171 /* The color from the theme. */
172 float *main_color = tip_colors[UI_TIP_LC_MAIN];
173 float *value_color = tip_colors[UI_TIP_LC_VALUE];
174 float *active_color = tip_colors[UI_TIP_LC_ACTIVE];
175 float *normal_color = tip_colors[UI_TIP_LC_NORMAL];
176 float *python_color = tip_colors[UI_TIP_LC_PYTHON];
177 float *alert_color = tip_colors[UI_TIP_LC_ALERT];
178
179 float background_color[3];
180
182
183 /* Draw background. */
184 ui_draw_tooltip_background(UI_style_get(), nullptr, &bbox);
185
186 /* set background_color */
187 rgb_uchar_to_float(background_color, theme->inner);
188
189 /* Calculate `normal_color`. */
190 rgb_uchar_to_float(main_color, theme->text);
191 copy_v3_v3(active_color, main_color);
192 copy_v3_v3(normal_color, main_color);
193 copy_v3_v3(python_color, main_color);
194 copy_v3_v3(alert_color, main_color);
195 copy_v3_v3(value_color, main_color);
196
197 /* Find the brightness difference between background and text colors. */
198
199 const float tone_bg = rgb_to_grayscale(background_color);
200 // tone_fg = rgb_to_grayscale(main_color);
201
202 /* Mix the colors. */
203 rgb_tint(value_color, 0.0f, 0.0f, tone_bg, 0.2f); /* Light gray. */
204 rgb_tint(active_color, 0.6f, 0.2f, tone_bg, 0.2f); /* Light blue. */
205 rgb_tint(normal_color, 0.0f, 0.0f, tone_bg, 0.4f); /* Gray. */
206 rgb_tint(python_color, 0.0f, 0.0f, tone_bg, 0.5f); /* Dark gray. */
207 rgb_tint(alert_color, 0.0f, 0.8f, tone_bg, 0.1f); /* Red. */
208
209 /* Draw text. */
210 BLF_wordwrap(data->fstyle.uifont_id, data->wrap_width);
211 BLF_wordwrap(blf_mono_font, data->wrap_width);
212
213 bbox.xmin += 0.5f * pad_px; /* add padding to the text */
214 bbox.ymax -= 0.25f * pad_px;
215
216 for (int i = 0; i < data->fields.size(); i++) {
217 const uiTooltipField *field = &data->fields[i];
218
219 bbox.ymin = bbox.ymax - (data->lineh * field->geom.lines);
220 if (field->format.style == UI_TIP_STYLE_HEADER) {
221 uiFontStyleDraw_Params fs_params{};
222 fs_params.align = UI_STYLE_TEXT_LEFT;
223 fs_params.word_wrap = true;
224
225 /* Draw header and active data (is done here to be able to change color). */
226 rgb_float_to_uchar(drawcol, tip_colors[UI_TIP_LC_MAIN]);
227 UI_fontstyle_set(&data->fstyle);
229 &data->fstyle, &bbox, field->text.c_str(), field->text.size(), drawcol, &fs_params);
230
231 /* Offset to the end of the last line. */
232 if (!field->text_suffix.empty()) {
233 const float xofs = field->geom.x_pos;
234 const float yofs = data->lineh * (field->geom.lines - 1);
235 bbox.xmin += xofs;
236 bbox.ymax -= yofs;
237
238 rgb_float_to_uchar(drawcol, tip_colors[UI_TIP_LC_ACTIVE]);
239 UI_fontstyle_draw(&data->fstyle,
240 &bbox,
241 field->text_suffix.c_str(),
242 field->text_suffix.size(),
243 drawcol,
244 &fs_params);
245
246 /* Undo offset. */
247 bbox.xmin -= xofs;
248 bbox.ymax += yofs;
249 }
250 }
251 else if (field->format.style == UI_TIP_STYLE_MONO) {
252 uiFontStyleDraw_Params fs_params{};
253 fs_params.align = UI_STYLE_TEXT_LEFT;
254 fs_params.word_wrap = true;
255 uiFontStyle fstyle_mono = data->fstyle;
256 fstyle_mono.uifont_id = blf_mono_font;
257
258 UI_fontstyle_set(&fstyle_mono);
259 /* XXX: needed because we don't have mono in 'U.uifonts'. */
260 BLF_size(fstyle_mono.uifont_id, fstyle_mono.points * UI_SCALE_FAC);
261 rgb_float_to_uchar(drawcol, tip_colors[int(field->format.color_id)]);
263 &fstyle_mono, &bbox, field->text.c_str(), field->text.size(), drawcol, &fs_params);
264 }
265 else if (field->format.style == UI_TIP_STYLE_IMAGE && field->image.has_value()) {
266
267 bbox.ymax -= field->image->height;
268
269 if (field->image->background == uiTooltipImageBackground::Checkerboard_Themed) {
270 imm_draw_box_checker_2d(float(bbox.xmin),
271 float(bbox.ymax),
272 float(bbox.xmin + field->image->width),
273 float(bbox.ymax + field->image->height));
274 }
275 else if (field->image->background == uiTooltipImageBackground::Checkerboard_Fixed) {
276 const float checker_dark = UI_ALPHA_CHECKER_DARK / 255.0f;
277 const float checker_light = UI_ALPHA_CHECKER_LIGHT / 255.0f;
278 const float color1[4] = {checker_dark, checker_dark, checker_dark, 1.0f};
279 const float color2[4] = {checker_light, checker_light, checker_light, 1.0f};
280 imm_draw_box_checker_2d_ex(float(bbox.xmin + U.pixelsize),
281 float(bbox.ymax + U.pixelsize),
282 float(bbox.xmin + field->image->width),
283 float(bbox.ymax + field->image->height),
284 color1,
285 color2,
286 8);
287 }
288
289 GPU_blend((field->image->premultiplied) ? GPU_BLEND_ALPHA_PREMULT : GPU_BLEND_ALPHA);
290
293 bbox.xmin,
294 bbox.ymax,
295 field->image->ibuf->x,
296 field->image->ibuf->y,
297 GPU_RGBA8,
298 true,
299 field->image->ibuf->byte_buffer.data,
300 1.0f,
301 1.0f,
302 float(field->image->width) / float(field->image->ibuf->x),
303 float(field->image->height) / float(field->image->ibuf->y),
304 (field->image->text_color) ? main_color : nullptr);
305
306 if (field->image->border) {
311 float border_color[4] = {1.0f, 1.0f, 1.0f, 0.15f};
312 float bgcolor[4];
314 if (rgb_to_grayscale(bgcolor) > 0.5f) {
315 border_color[0] = 0.0f;
316 border_color[1] = 0.0f;
317 border_color[2] = 0.0f;
318 }
319 immUniformColor4fv(border_color);
321 float(bbox.xmin),
322 float(bbox.ymax),
323 float(bbox.xmin + field->image->width),
324 float(bbox.ymax + field->image->height));
327 }
328 }
329 else if (field->format.style == UI_TIP_STYLE_SPACER) {
330 bbox.ymax -= data->lineh * UI_TIP_SPACER;
331 }
332 else {
334 uiFontStyleDraw_Params fs_params{};
335 fs_params.align = UI_STYLE_TEXT_LEFT;
336 fs_params.word_wrap = true;
337
338 /* Draw remaining data. */
339 rgb_float_to_uchar(drawcol, tip_colors[int(field->format.color_id)]);
340 UI_fontstyle_set(&data->fstyle);
342 &data->fstyle, &bbox, field->text.c_str(), field->text.size(), drawcol, &fs_params);
343 }
344
345 bbox.ymax -= data->lineh * field->geom.lines;
346 }
347
348 BLF_disable(data->fstyle.uifont_id, BLF_WORD_WRAP);
350}
351
353{
354 /* Put ownership back into a unique pointer. */
355 std::unique_ptr<uiTooltipData> data{static_cast<uiTooltipData *>(region->regiondata)};
356 for (uiTooltipField &field : data->fields) {
357 if (field.image && field.image->ibuf) {
358 IMB_freeImBuf(field.image->ibuf);
359 }
360 }
361 region->regiondata = nullptr;
362}
363
365
366/* -------------------------------------------------------------------- */
369
372 PointerRNA *opptr)
373{
374 std::string str = WM_operator_pystring_ex(C, nullptr, false, false, ot, opptr);
375
376 /* Avoid overly verbose tips (eg, arrays of 20 layers), exact limit is arbitrary. */
377 return WM_operator_pystring_abbreviate(std::move(str), 32);
378}
379
381
382/* -------------------------------------------------------------------- */
385
386#ifdef WITH_PYTHON
387
388static bool ui_tooltip_data_append_from_keymap(bContext *C, uiTooltipData &data, wmKeyMap *keymap)
389{
390 const int fields_len_init = data.fields.size();
391
392 LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) {
393 wmOperatorType *ot = WM_operatortype_find(kmi->idname, true);
394 if (!ot) {
395 continue;
396 }
397 /* Tip. */
399 ot->description ? ot->description : ot->name,
400 {},
403 true);
404
405 /* Shortcut. */
406 const std::string kmi_str = WM_keymap_item_to_string(kmi, false).value_or("None");
408 fmt::format(TIP_("Shortcut: {}"), kmi_str),
409 {},
412
413 /* Python. */
414 if (U.flag & USER_TOOLTIPS_PYTHON) {
415 std::string str = ui_tooltip_text_python_from_op(C, ot, kmi->ptr);
417 data, fmt::format(TIP_("Python: {}"), str), {}, UI_TIP_STYLE_MONO, UI_TIP_LC_PYTHON);
418 }
419 }
420
421 return (fields_len_init != data.fields.size());
422}
423
424#endif /* WITH_PYTHON */
425
427{
428 if (tip.is_empty()) {
429 return false;
430 }
431
432 /* Already ends with punctuation. */
433 if (ELEM(tip.back(), '.', '!', '?')) {
434 return false;
435 }
436
437 /* Contains a bullet Unicode character. */
438 if (tip.find("\xe2\x80\xa2") != blender::StringRef::not_found) {
439 return false;
440 }
441
442 return true;
443}
444
448static std::unique_ptr<uiTooltipData> ui_tooltip_data_from_tool(bContext *C,
449 uiBut *but,
450 bool is_label)
451{
452 if (but->optype == nullptr) {
453 return nullptr;
454 }
455 /* While this should always be set for buttons as they are shown in the UI,
456 * the operator search popup can create a button that has no properties, see: #112541. */
457 if (but->opptr == nullptr) {
458 return nullptr;
459 }
460
461 if (!STREQ(but->optype->idname, "WM_OT_tool_set_by_id")) {
462 return nullptr;
463 }
464
465 /* Needed to get the space-data's type (below). */
466 if (CTX_wm_space_data(C) == nullptr) {
467 return nullptr;
468 }
469
470 char tool_id[MAX_NAME];
471 RNA_string_get(but->opptr, "name", tool_id);
472 BLI_assert(tool_id[0] != '\0');
473
474 /* When false, we're in a different space type to the tool being set.
475 * Needed for setting the fallback tool from the properties space.
476 *
477 * If we drop the hard coded 3D-view in properties hack, we can remove this check. */
478 bool has_valid_context = true;
479 const char *has_valid_context_error = IFACE_("Unsupported context");
480 {
481 ScrArea *area = CTX_wm_area(C);
482 if (area == nullptr) {
483 has_valid_context = false;
484 }
485 else {
486 PropertyRNA *prop = RNA_struct_find_property(but->opptr, "space_type");
487 if (RNA_property_is_set(but->opptr, prop)) {
488 const int space_type_prop = RNA_property_enum_get(but->opptr, prop);
489 if (space_type_prop != area->spacetype) {
490 has_valid_context = false;
491 }
492 }
493 }
494 }
495
496 /* We have a tool, now extract the info. */
497 std::unique_ptr<uiTooltipData> data = std::make_unique<uiTooltipData>();
498
499#ifdef WITH_PYTHON
500 /* It turns out to be most simple to do this via Python since C
501 * doesn't have access to information about non-active tools. */
502
503 /* Title (when icon-only). */
504 if (but->drawstr.empty()) {
505 const char *expr_imports[] = {"bpy", "bl_ui", nullptr};
506 char expr[256];
507 SNPRINTF(expr,
508 "bl_ui.space_toolsystem_common.item_from_id("
509 "bpy.context, "
510 "bpy.context.space_data.type, "
511 "'%s').label",
512 tool_id);
513 char *expr_result = nullptr;
514 bool is_error = false;
515
516 if (has_valid_context == false) {
517 expr_result = BLI_strdup(has_valid_context_error);
518 }
519 else if (BPY_run_string_as_string(C, expr_imports, expr, nullptr, &expr_result)) {
520 if (STREQ(expr_result, "")) {
521 MEM_freeN(expr_result);
522 expr_result = nullptr;
523 }
524 }
525 else {
526 /* NOTE: this is an exceptional case, we could even remove it
527 * however there have been reports of tooltips failing, so keep it for now. */
528 expr_result = BLI_strdup(IFACE_("Internal error!"));
529 is_error = true;
530 }
531
532 if (expr_result != nullptr) {
533 /* NOTE: This is a very weak hack to get a valid translation most of the time...
534 * Proper way to do would be to get i18n context from the item, somehow. */
535 const char *label_str = CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, expr_result);
536 if (label_str == expr_result) {
537 label_str = IFACE_(expr_result);
538 }
539
540 if (label_str != expr_result) {
541 MEM_freeN(expr_result);
542 expr_result = BLI_strdup(label_str);
543 }
544
546 expr_result,
547 {},
549 (is_error) ? UI_TIP_LC_ALERT : UI_TIP_LC_MAIN,
550 false);
551 MEM_freeN(expr_result);
552 }
553 }
554
555 /* Tip. */
556 if (is_label == false) {
557 const char *expr_imports[] = {"bpy", "bl_ui", nullptr};
558 char expr[256];
559 SNPRINTF(expr,
560 "bl_ui.space_toolsystem_common.description_from_id("
561 "bpy.context, "
562 "bpy.context.space_data.type, "
563 "'%s')",
564 tool_id);
565
566 char *expr_result = nullptr;
567 bool is_error = false;
568
569 if (has_valid_context == false) {
570 expr_result = BLI_strdup(has_valid_context_error);
571 }
572 else if (BPY_run_string_as_string(C, expr_imports, expr, nullptr, &expr_result)) {
573 if (STREQ(expr_result, "")) {
574 MEM_freeN(expr_result);
575 expr_result = nullptr;
576 }
577 }
578 else {
579 /* NOTE: this is an exceptional case, we could even remove it
580 * however there have been reports of tooltips failing, so keep it for now. */
581 expr_result = BLI_strdup(TIP_("Internal error!"));
582 is_error = true;
583 }
584
585 if (expr_result != nullptr) {
586 const bool add_period = ui_tooltip_period_needed(expr_result);
588 fmt::format("{}{}", expr_result, add_period ? "." : ""),
589 {},
591 (is_error) ? UI_TIP_LC_ALERT : UI_TIP_LC_MAIN,
592 true);
593 MEM_freeN(expr_result);
594 }
595 }
596
597 /* Shortcut. */
598 const bool show_shortcut = is_label == false &&
600
601 if (show_shortcut) {
602 /* There are different kinds of shortcuts:
603 *
604 * - Direct access to the tool (as if the toolbar button is pressed).
605 * - The key is assigned to the operator itself
606 * (bypassing the tool, executing the operator).
607 *
608 * Either way case it's useful to show the shortcut.
609 */
610 std::string shortcut = UI_but_string_get_operator_keymap(*C, *but);
611
612 if (shortcut.empty()) {
613 /* Check for direct access to the tool. */
614 if (std::optional<std::string> shortcut_toolbar = WM_key_event_operator_string(
615 C, "WM_OT_toolbar", WM_OP_INVOKE_REGION_WIN, nullptr, true))
616 {
617 /* Generate keymap in order to inspect it.
618 * NOTE: we could make a utility to avoid the keymap generation part of this. */
619 const char *expr_imports[] = {
620 "bpy", "bl_keymap_utils", "bl_keymap_utils.keymap_from_toolbar", nullptr};
621 const char *expr =
622 ("getattr("
623 "bl_keymap_utils.keymap_from_toolbar.generate("
624 "bpy.context, "
625 "bpy.context.space_data.type), "
626 "'as_pointer', lambda: 0)()");
627
628 intptr_t expr_result = 0;
629
630 if (has_valid_context == false) {
631 shortcut = has_valid_context_error;
632 }
633 else if (BPY_run_string_as_intptr(C, expr_imports, expr, nullptr, &expr_result)) {
634 if (expr_result != 0) {
635 wmKeyMap *keymap = (wmKeyMap *)expr_result;
636 LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) {
637 if (STREQ(kmi->idname, but->optype->idname)) {
638 char tool_id_test[MAX_NAME];
639 RNA_string_get(kmi->ptr, "name", tool_id_test);
640 if (STREQ(tool_id, tool_id_test)) {
641 std::string kmi_str = WM_keymap_item_to_string(kmi, false).value_or("");
642 shortcut = fmt::format("{}, {}", *shortcut_toolbar, kmi_str);
643 break;
644 }
645 }
646 }
647 }
648 }
649 else {
650 BLI_assert(0);
651 }
652 }
653 }
654
655 if (!shortcut.empty()) {
657 fmt::format(TIP_("Shortcut: {}"), shortcut),
658 {},
661 true);
662 }
663 }
664
665 if (show_shortcut) {
666 /* Shortcut for Cycling
667 *
668 * As a second option, we may have a shortcut to cycle this tool group.
669 *
670 * Since some keymaps may use this for the primary means of binding keys,
671 * it's useful to show these too.
672 * Without this there is no way to know how to use a key to set the tool.
673 *
674 * This is a little involved since the shortcut may be bound to another tool in this group,
675 * instead of the current tool on display. */
676
677 char *expr_result = nullptr;
678 size_t expr_result_len;
679
680 {
681 const char *expr_imports[] = {"bpy", "bl_ui", nullptr};
682 char expr[256];
683 SNPRINTF(expr,
684 "'\\x00'.join("
685 "item.idname for item in bl_ui.space_toolsystem_common.item_group_from_id("
686 "bpy.context, "
687 "bpy.context.space_data.type, '%s', coerce=True) "
688 "if item is not None)",
689 tool_id);
690
691 if (has_valid_context == false) {
692 /* pass */
693 }
695 C, expr_imports, expr, nullptr, &expr_result, &expr_result_len))
696 {
697 /* pass. */
698 }
699 }
700
701 if (expr_result != nullptr) {
702 PointerRNA op_props;
704 RNA_boolean_set(&op_props, "cycle", true);
705
706 std::optional<std::string> shortcut;
707
708 const char *item_end = expr_result + expr_result_len;
709 const char *item_step = expr_result;
710
711 while (item_step < item_end) {
712 RNA_string_set(&op_props, "name", item_step);
714 but->optype->idname,
716 static_cast<IDProperty *>(op_props.data),
717 true);
718 if (shortcut) {
719 break;
720 }
721 item_step += strlen(item_step) + 1;
722 }
723
725 MEM_freeN(expr_result);
726
727 if (shortcut) {
729 fmt::format(TIP_("Shortcut Cycle: {}"), *shortcut),
730 {},
733 true);
734 }
735 }
736 }
737
738 /* Python */
739 if ((is_label == false) && (U.flag & USER_TOOLTIPS_PYTHON)) {
740 std::string str = ui_tooltip_text_python_from_op(C, but->optype, but->opptr);
742 fmt::format(TIP_("Python: {}"), str),
743 {},
746 true);
747 }
748
749 /* Keymap */
750
751 /* This is too handy not to expose somehow, let's be sneaky for now. */
752 if ((is_label == false) && CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) {
753 const char *expr_imports[] = {"bpy", "bl_ui", nullptr};
754 char expr[256];
755 SNPRINTF(expr,
756 "getattr("
757 "bl_ui.space_toolsystem_common.keymap_from_id("
758 "bpy.context, "
759 "bpy.context.space_data.type, "
760 "'%s'), "
761 "'as_pointer', lambda: 0)()",
762 tool_id);
763
764 intptr_t expr_result = 0;
765
766 if (has_valid_context == false) {
767 /* pass */
768 }
769 else if (BPY_run_string_as_intptr(C, expr_imports, expr, nullptr, &expr_result)) {
770 if (expr_result != 0) {
772 *data, TIP_("Tool Keymap:"), {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_NORMAL, true);
773 wmKeyMap *keymap = (wmKeyMap *)expr_result;
774 ui_tooltip_data_append_from_keymap(C, *data, keymap);
775 }
776 }
777 else {
778 BLI_assert(0);
779 }
780 }
781#else
782 UNUSED_VARS(is_label, has_valid_context, has_valid_context_error);
783#endif /* WITH_PYTHON */
784
785 return data->fields.is_empty() ? nullptr : std::move(data);
786}
787
788static std::unique_ptr<uiTooltipData> ui_tooltip_data_from_button_or_extra_icon(
789 bContext *C, uiBut *but, uiButExtraOpIcon *extra_icon, const bool is_label)
790{
791 char buf[512];
792
793 wmOperatorType *optype = extra_icon ? UI_but_extra_operator_icon_optype_get(extra_icon) :
794 but->optype;
795 PropertyRNA *rnaprop = extra_icon ? nullptr : but->rnaprop;
796
797 std::unique_ptr<uiTooltipData> data = std::make_unique<uiTooltipData>();
798
799 /* Menus already show shortcuts, don't show them in the tool-tips. */
800 const bool is_menu = ui_block_is_menu(but->block) && !ui_block_is_pie_menu(but->block);
801
802 std::string but_label;
803 std::string but_tip;
804 std::string but_tip_label;
805 std::string op_keymap;
806 std::string prop_keymap;
807 std::string rna_struct;
808 std::string rna_prop;
809 std::string enum_label;
810 std::string enum_tip;
811
812 if (extra_icon) {
813 if (is_label) {
814 but_label = UI_but_extra_icon_string_get_label(*extra_icon);
815 }
816 else {
817 but_label = UI_but_extra_icon_string_get_label(*extra_icon);
818 but_tip = UI_but_extra_icon_string_get_tooltip(*C, *extra_icon);
819 if (!is_menu) {
820 op_keymap = UI_but_extra_icon_string_get_operator_keymap(*C, *extra_icon);
821 }
822 }
823 }
824 else {
825 const std::optional<EnumPropertyItem> enum_item = UI_but_rna_enum_item_get(*C, *but);
826 if (is_label) {
827 but_tip_label = UI_but_string_get_tooltip_label(*but);
828 but_label = UI_but_string_get_label(*but);
829 enum_label = enum_item ? enum_item->name : "";
830 }
831 else {
832 but_label = UI_but_string_get_label(*but);
833 but_tip_label = UI_but_string_get_tooltip_label(*but);
834 but_tip = UI_but_string_get_tooltip(*C, *but);
835 enum_label = enum_item ? enum_item->name : "";
836 const char *description_c = enum_item ? enum_item->description : nullptr;
837 enum_tip = description_c ? description_c : "";
838 if (!is_menu) {
839 op_keymap = UI_but_string_get_operator_keymap(*C, *but);
840 prop_keymap = UI_but_string_get_property_keymap(*C, *but);
841 }
844 }
845 }
846
847 /* Label: If there is a custom tooltip label, use that to override the label to display.
848 * Otherwise fallback to the regular label. */
849 if (!but_tip_label.empty()) {
851 }
852 /* Regular (non-custom) label. Only show when the button doesn't already show the label. Check
853 * prefix instead of comparing because the button may include the shortcut. Buttons with dynamic
854 * tool-tips also don't get their default label here since they can already provide more accurate
855 * and specific tool-tip content. */
856 else if (!but_label.empty() && !blender::StringRef(but->drawstr).startswith(but_label) &&
857 !but->tip_func)
858 {
860 }
861
862 /* Tip */
863 if (!but_tip.empty()) {
864 if (!enum_label.empty()) {
866 *data, fmt::format("{}: ", but_tip), enum_label, UI_TIP_STYLE_HEADER, UI_TIP_LC_NORMAL);
867 }
868 else {
869 const bool add_period = ui_tooltip_period_needed(but_tip);
871 fmt::format("{}{}", but_tip, add_period ? "." : ""),
872 {},
875 }
876
877 /* special case enum rna buttons */
878 if ((but->type & UI_BTYPE_ROW) && rnaprop && RNA_property_flag(rnaprop) & PROP_ENUM_FLAG) {
880 TIP_("(Shift-Click/Drag to select multiple)"),
881 {},
884 }
885 }
886 /* When there is only an enum label (no button label or tip), draw that as header. */
887 else if (!enum_label.empty() && but_label.empty()) {
889 *data, std::move(enum_label), {}, UI_TIP_STYLE_HEADER, UI_TIP_LC_NORMAL);
890 }
891
892 /* Don't include further details if this is just a quick label tooltip. */
893 if (is_label) {
894 return data->fields.is_empty() ? nullptr : std::move(data);
895 }
896
897 /* Enum field label & tip. */
898 if (!enum_tip.empty()) {
900 *data, std::move(enum_tip), {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_VALUE);
901 }
902
903 /* Operator shortcut. */
904 if (!op_keymap.empty()) {
906 fmt::format(TIP_("Shortcut: {}"), op_keymap),
907 {},
910 true);
911 }
912
913 /* Property context-toggle shortcut. */
914 if (!prop_keymap.empty()) {
916 fmt::format(TIP_("Shortcut: {}"), prop_keymap),
917 {},
920 true);
921 }
922
924 /* Better not show the value of a password. */
925 if ((rnaprop && (RNA_property_subtype(rnaprop) == PROP_PASSWORD)) == 0) {
926 /* Full string. */
927 ui_but_string_get(but, buf, sizeof(buf));
928 if (buf[0]) {
930 fmt::format(TIP_("Value: {}"), buf),
931 {},
934 true);
935 }
936 }
937 }
938
939 if (rnaprop) {
940 const int unit_type = UI_but_unit_type_get(but);
941
942 if (unit_type == PROP_UNIT_ROTATION) {
943 if (RNA_property_type(rnaprop) == PROP_FLOAT) {
944 float value = RNA_property_array_check(rnaprop) ?
945 RNA_property_float_get_index(&but->rnapoin, rnaprop, but->rnaindex) :
946 RNA_property_float_get(&but->rnapoin, rnaprop);
948 fmt::format(TIP_("Radians: {}"), value),
949 {},
952 }
953 }
954
955 if (but->flag & UI_BUT_DRIVEN) {
956 if (ui_but_anim_expression_get(but, buf, sizeof(buf))) {
958 fmt::format(TIP_("Expression: {}"), buf),
959 {},
962 }
963 }
964
965 if (but->rnapoin.owner_id) {
966 const ID *id = but->rnapoin.owner_id;
967 if (ID_IS_LINKED(id)) {
969 fmt::format(TIP_("Library: {}"), id->lib->filepath),
970 {},
973 }
974 }
975 }
976 else if (optype) {
977 PointerRNA *opptr = extra_icon ? UI_but_extra_operator_icon_opptr_get(extra_icon) :
978 /* Allocated when needed, the button owns it. */
980
981 /* So the context is passed to field functions (some Python field functions use it). */
983
984 std::string str = ui_tooltip_text_python_from_op(C, optype, opptr);
985
986 /* Operator info. */
987 if (U.flag & USER_TOOLTIPS_PYTHON) {
989 fmt::format(TIP_("Python: {}"), str),
990 {},
993 true);
994 }
995 }
996
997 /* Button is disabled, we may be able to tell user why. */
998 if ((but->flag & UI_BUT_DISABLED) || extra_icon) {
999 const char *disabled_msg_orig = nullptr;
1000 const char *disabled_msg = nullptr;
1001 bool disabled_msg_free = false;
1002
1003 /* If operator poll check failed, it can give pretty precise info why. */
1004 if (optype) {
1005 const wmOperatorCallContext opcontext = extra_icon ? extra_icon->optype_params->opcontext :
1006 but->opcontext;
1007 wmOperatorCallParams call_params{};
1008 call_params.optype = optype;
1009 call_params.opcontext = opcontext;
1011 ui_but_context_poll_operator_ex(C, but, &call_params);
1012 disabled_msg_orig = CTX_wm_operator_poll_msg_get(C, &disabled_msg_free);
1013 disabled_msg = TIP_(disabled_msg_orig);
1014 }
1015 /* Alternatively, buttons can store some reasoning too. */
1016 else if (!extra_icon && but->disabled_info) {
1017 disabled_msg = TIP_(but->disabled_info);
1018 }
1019
1020 if (disabled_msg && disabled_msg[0]) {
1022 fmt::format(TIP_("Disabled: {}"), disabled_msg),
1023 {},
1026 }
1027 if (disabled_msg_free) {
1028 MEM_freeN((void *)disabled_msg_orig);
1029 }
1030 }
1031
1032 if ((U.flag & USER_TOOLTIPS_PYTHON) && !optype && !rna_struct.empty()) {
1033 {
1035 rna_prop.empty() ?
1036 fmt::format(TIP_("Python: {}"), rna_struct) :
1037 fmt::format(TIP_("Python: {}.{}"), rna_struct, rna_prop),
1038 {},
1041 (data->fields.size() > 0));
1042 }
1043
1044 if (but->rnapoin.owner_id) {
1045 std::optional<std::string> str = rnaprop ? RNA_path_full_property_py_ex(
1046 &but->rnapoin, rnaprop, but->rnaindex, true) :
1049 }
1050 }
1051
1052 if (but->type == UI_BTYPE_COLOR) {
1053
1054 float color[4];
1055 ui_but_v3_get(but, color);
1056 color[3] = 1.0f;
1057
1058 if (but->rnaprop) {
1059 BLI_assert(but->rnaindex == -1);
1060 if (RNA_property_array_length(&but->rnapoin, but->rnaprop) == 4) {
1062 }
1063 }
1064
1065 if (!ui_but_is_color_gamma(but)) {
1067 }
1068
1069 uchar rgb_hex_uchar[4];
1070 rgba_float_to_uchar(rgb_hex_uchar, color);
1071 const std::string hex_st = fmt::format("Hex: #{:02X}{:02X}{:02X}{:02X}",
1072 int(rgb_hex_uchar[0]),
1073 int(rgb_hex_uchar[1]),
1074 int(rgb_hex_uchar[2]),
1075 int(rgb_hex_uchar[3]));
1076
1077 const std::string rgba_st = fmt::format("{}: {:.3f} {:.3f} {:.3f} {:.3f}",
1078 TIP_("RGBA"),
1079 color[0],
1080 color[1],
1081 color[2],
1082 color[3]);
1083 float hsva[4];
1084 rgb_to_hsv_v(color, hsva);
1085 hsva[3] = color[3];
1086 const std::string hsva_st = fmt::format(
1087 "{}: {:.3f} {:.3f} {:.3f} {:.3f}", TIP_("HSVA"), hsva[0], hsva[1], hsva[2], hsva[3]);
1088
1089 const uiFontStyle *fs = &UI_style_get()->tooltip;
1091 float w = BLF_width(blf_mono_font, hsva_st.c_str(), hsva_st.size());
1092
1093 uiTooltipImage image_data;
1094 image_data.width = int(w);
1095 image_data.height = int(w / 4.0f);
1096 image_data.ibuf = IMB_allocImBuf(image_data.width, image_data.height, 32, IB_rect);
1097 image_data.border = true;
1098 image_data.premultiplied = false;
1099
1101 if (color[3] == 1.0f) {
1102 /* No transparency so draw the entire area solid without checkerboard. */
1105 image_data.ibuf, color, 1, 1, image_data.width, image_data.height, display);
1106 }
1107 else {
1109 /* Draw one half with transparency. */
1110 IMB_rectfill_area(image_data.ibuf,
1111 color,
1112 image_data.width / 2,
1113 1,
1114 image_data.width,
1115 image_data.height,
1116 display);
1117 /* Draw the other half with a solid color. */
1118 color[3] = 1.0f;
1120 image_data.ibuf, color, 1, 1, image_data.width / 2, image_data.height, display);
1121 }
1122
1125 UI_tooltip_image_field_add(*data, image_data);
1131
1132 /* Tooltip now owns a copy of the ImBuf, so we can delete ours.*/
1133 IMB_freeImBuf(image_data.ibuf);
1134 }
1135
1136 return data->fields.is_empty() ? nullptr : std::move(data);
1137}
1138
1139static std::unique_ptr<uiTooltipData> ui_tooltip_data_from_gizmo(bContext *C, wmGizmo *gz)
1140{
1141 std::unique_ptr<uiTooltipData> data = std::make_unique<uiTooltipData>();
1142
1143 /* TODO(@ideasman42): a way for gizmos to have their own descriptions (low priority). */
1144
1145 /* Operator Actions */
1146 {
1147 const bool use_drag = gz->drag_part != -1 && gz->highlight_part != gz->drag_part;
1148 struct GizmoOpActions {
1149 int part;
1150 const char *prefix;
1151 };
1152 GizmoOpActions gzop_actions[] = {
1153 {
1154 gz->highlight_part,
1155 use_drag ? CTX_TIP_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Click") : nullptr,
1156 },
1157 {
1158 use_drag ? gz->drag_part : -1,
1159 use_drag ? CTX_TIP_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Drag") : nullptr,
1160 },
1161 };
1162
1163 for (int i = 0; i < ARRAY_SIZE(gzop_actions); i++) {
1164 wmGizmoOpElem *gzop = (gzop_actions[i].part != -1) ?
1165 WM_gizmo_operator_get(gz, gzop_actions[i].part) :
1166 nullptr;
1167 if (gzop != nullptr) {
1168 /* Description */
1169 std::string info = WM_operatortype_description_or_name(C, gzop->type, &gzop->ptr);
1170
1171 if (!info.empty()) {
1173 *data,
1174 gzop_actions[i].prefix ? fmt::format("{}: {}", gzop_actions[i].prefix, info) : info,
1175 {},
1178 false);
1179 }
1180
1181 /* Shortcut */
1182 {
1183 IDProperty *prop = static_cast<IDProperty *>(gzop->ptr.data);
1184 if (std::optional<std::string> shortcut_str = WM_key_event_operator_string(
1185 C, gzop->type->idname, WM_OP_INVOKE_DEFAULT, prop, true))
1186 {
1188 fmt::format(TIP_("Shortcut: {}"), *shortcut_str),
1189 {},
1192 true);
1193 }
1194 }
1195 }
1196 }
1197 }
1198
1199 /* Property Actions */
1200 if (gz->type->target_property_defs_len) {
1202 for (int i = 0; i < gz->type->target_property_defs_len; i++) {
1203 /* TODO(@ideasman42): function callback descriptions. */
1204 wmGizmoProperty *gz_prop = &gz_prop_array[i];
1205 if (gz_prop->prop != nullptr) {
1206 const char *info = RNA_property_ui_description(gz_prop->prop);
1207 if (info && info[0]) {
1209 }
1210 }
1211 }
1212 }
1213
1214 return data->fields.is_empty() ? nullptr : std::move(data);
1215}
1216
1217static std::unique_ptr<uiTooltipData> ui_tooltip_data_from_custom_func(bContext *C, uiBut *but)
1218{
1219 /* Create tooltip data. */
1220 std::unique_ptr<uiTooltipData> data = std::make_unique<uiTooltipData>();
1221
1222 /* Create fields from custom callback. */
1223 but->tip_custom_func(*C, *data, but->tip_arg);
1224
1225 return data->fields.is_empty() ? nullptr : std::move(data);
1226}
1227
1229 std::unique_ptr<uiTooltipData> data_uptr,
1230 const float init_position[2],
1231 const rcti *init_rect_overlap)
1232{
1233 const float pad_px = UI_TIP_PADDING;
1234 wmWindow *win = CTX_wm_window(C);
1235 const blender::int2 win_size = WM_window_native_pixel_size(win);
1236 const uiStyle *style = UI_style_get();
1237 rcti rect_i;
1238 int font_flag = 0;
1239
1240 /* Create area region. */
1242
1243 static ARegionType type;
1244 memset(&type, 0, sizeof(ARegionType));
1248 region->type = &type;
1249 /* Move ownership to region data. The region type free callback puts it back into a unique
1250 * pointer for save freeing. */
1251 region->regiondata = data_uptr.release();
1252
1253 uiTooltipData *data = static_cast<uiTooltipData *>(region->regiondata);
1254
1255 /* Set font, get bounding-box. */
1256 data->fstyle = style->tooltip; /* copy struct */
1257
1258 UI_fontstyle_set(&data->fstyle);
1259
1260 data->wrap_width = min_ii(UI_TIP_MAXWIDTH * U.pixelsize, win_size[0] - (UI_TIP_PADDING * 2));
1261
1262 font_flag |= BLF_WORD_WRAP;
1263 BLF_enable(data->fstyle.uifont_id, font_flag);
1264 BLF_enable(blf_mono_font, font_flag);
1265 BLF_wordwrap(data->fstyle.uifont_id, data->wrap_width);
1266 BLF_wordwrap(blf_mono_font, data->wrap_width);
1267
1268 /* These defines tweaked depending on font. */
1269#define TIP_BORDER_X (16.0f)
1270#define TIP_BORDER_Y (6.0f)
1271
1272 int h = BLF_height_max(data->fstyle.uifont_id);
1273
1274 int i, fonth, fontw;
1275 for (i = 0, fontw = 0, fonth = 0; i < data->fields.size(); i++) {
1276 uiTooltipField *field = &data->fields[i];
1277 ResultBLF info = {0};
1278 int w = 0;
1279 int x_pos = 0;
1280 int font_id;
1281
1282 if (field->format.style == UI_TIP_STYLE_MONO) {
1283 BLF_size(blf_mono_font, data->fstyle.points * UI_SCALE_FAC);
1284 font_id = blf_mono_font;
1285 }
1286 else {
1287 font_id = data->fstyle.uifont_id;
1288 }
1289
1290 if (!field->text.empty()) {
1291 w = BLF_width(font_id, field->text.c_str(), field->text.size(), &info);
1292 }
1293
1294 /* check for suffix (enum label) */
1295 if (!field->text_suffix.empty()) {
1296 x_pos = info.width;
1297 w = max_ii(
1298 w, x_pos + BLF_width(font_id, field->text_suffix.c_str(), field->text_suffix.size()));
1299 }
1300
1301 fonth += h * info.lines;
1302
1303 if (field->format.style == UI_TIP_STYLE_SPACER) {
1304 fonth += h * UI_TIP_SPACER;
1305 }
1306
1307 if (field->format.style == UI_TIP_STYLE_IMAGE && field->image) {
1308 fonth += field->image->height;
1309 w = max_ii(w, field->image->width);
1310 }
1311
1312 fontw = max_ii(fontw, w);
1313
1314 field->geom.lines = info.lines;
1315 field->geom.x_pos = x_pos;
1316 }
1317
1318 BLF_disable(data->fstyle.uifont_id, font_flag);
1319 BLF_disable(blf_mono_font, font_flag);
1320
1321 data->toth = fonth;
1322 data->lineh = h;
1323
1324 /* Compute position. */
1325 {
1326 rctf rect_fl;
1327 rect_fl.xmin = init_position[0] - TIP_BORDER_X;
1328 rect_fl.xmax = rect_fl.xmin + fontw + pad_px;
1329 rect_fl.ymax = init_position[1] - TIP_BORDER_Y;
1330 rect_fl.ymin = rect_fl.ymax - fonth - TIP_BORDER_Y;
1331 BLI_rcti_rctf_copy(&rect_i, &rect_fl);
1332 }
1333
1334#undef TIP_BORDER_X
1335#undef TIP_BORDER_Y
1336
1337 // #define USE_ALIGN_Y_CENTER
1338
1339 /* Clamp to window bounds. */
1340 {
1341 /* Ensure at least 5 px above screen bounds.
1342 * #UI_UNIT_Y is just a guess to be above the menu item. */
1343 if (init_rect_overlap != nullptr) {
1344 const int pad = max_ff(1.0f, U.pixelsize) * 5;
1345 rcti init_rect;
1346 init_rect.xmin = init_rect_overlap->xmin - pad;
1347 init_rect.xmax = init_rect_overlap->xmax + pad;
1348 init_rect.ymin = init_rect_overlap->ymin - pad;
1349 init_rect.ymax = init_rect_overlap->ymax + pad;
1350 rcti rect_clamp;
1351 rect_clamp.xmin = 0;
1352 rect_clamp.xmax = win_size[0];
1353 rect_clamp.ymin = 0;
1354 rect_clamp.ymax = win_size[1];
1355 /* try right. */
1356 const int size_x = BLI_rcti_size_x(&rect_i);
1357 const int size_y = BLI_rcti_size_y(&rect_i);
1358 const int cent_overlap_x = BLI_rcti_cent_x(&init_rect);
1359#ifdef USE_ALIGN_Y_CENTER
1360 const int cent_overlap_y = BLI_rcti_cent_y(&init_rect);
1361#endif
1362 struct {
1363 rcti xpos;
1364 rcti xneg;
1365 rcti ypos;
1366 rcti yneg;
1367 } rect;
1368
1369 { /* xpos */
1370 rcti r = rect_i;
1371 r.xmin = init_rect.xmax;
1372 r.xmax = r.xmin + size_x;
1373#ifdef USE_ALIGN_Y_CENTER
1374 r.ymin = cent_overlap_y - (size_y / 2);
1375 r.ymax = r.ymin + size_y;
1376#else
1377 r.ymin = init_rect.ymax - BLI_rcti_size_y(&rect_i);
1378 r.ymax = init_rect.ymax;
1379 r.ymin -= UI_POPUP_MARGIN;
1380 r.ymax -= UI_POPUP_MARGIN;
1381#endif
1382 rect.xpos = r;
1383 }
1384 { /* xneg */
1385 rcti r = rect_i;
1386 r.xmin = init_rect.xmin - size_x;
1387 r.xmax = r.xmin + size_x;
1388#ifdef USE_ALIGN_Y_CENTER
1389 r.ymin = cent_overlap_y - (size_y / 2);
1390 r.ymax = r.ymin + size_y;
1391#else
1392 r.ymin = init_rect.ymax - BLI_rcti_size_y(&rect_i);
1393 r.ymax = init_rect.ymax;
1394 r.ymin -= UI_POPUP_MARGIN;
1395 r.ymax -= UI_POPUP_MARGIN;
1396#endif
1397 rect.xneg = r;
1398 }
1399 { /* ypos */
1400 rcti r = rect_i;
1401 r.xmin = cent_overlap_x - (size_x / 2);
1402 r.xmax = r.xmin + size_x;
1403 r.ymin = init_rect.ymax;
1404 r.ymax = r.ymin + size_y;
1405 rect.ypos = r;
1406 }
1407 { /* yneg */
1408 rcti r = rect_i;
1409 r.xmin = cent_overlap_x - (size_x / 2);
1410 r.xmax = r.xmin + size_x;
1411 r.ymin = init_rect.ymin - size_y;
1412 r.ymax = r.ymin + size_y;
1413 rect.yneg = r;
1414 }
1415
1416 bool found = false;
1417 for (int j = 0; j < 4; j++) {
1418 const rcti *r = (&rect.xpos) + j;
1419 if (BLI_rcti_inside_rcti(&rect_clamp, r)) {
1420 rect_i = *r;
1421 found = true;
1422 break;
1423 }
1424 }
1425 if (!found) {
1426 /* Fallback, we could pick the best fallback, for now just use xpos. */
1427 int offset_dummy[2];
1428 rect_i = rect.xpos;
1429 BLI_rcti_clamp(&rect_i, &rect_clamp, offset_dummy);
1430 }
1431 }
1432 else {
1433 const int pad = max_ff(1.0f, U.pixelsize) * 5;
1434 rcti rect_clamp;
1435 rect_clamp.xmin = pad;
1436 rect_clamp.xmax = win_size[0] - pad;
1437 rect_clamp.ymin = pad + (UI_UNIT_Y * 2);
1438 rect_clamp.ymax = win_size[1] - pad;
1439 int offset_dummy[2];
1440 BLI_rcti_clamp(&rect_i, &rect_clamp, offset_dummy);
1441 }
1442 }
1443
1444#undef USE_ALIGN_Y_CENTER
1445
1446 /* add padding */
1447 BLI_rcti_resize(&rect_i, BLI_rcti_size_x(&rect_i) + pad_px, BLI_rcti_size_y(&rect_i) + pad_px);
1448
1449 /* widget rect, in region coords */
1450 {
1451 /* Compensate for margin offset, visually this corrects the position. */
1452 const int margin = UI_POPUP_MARGIN;
1453 if (init_rect_overlap != nullptr) {
1454 BLI_rcti_translate(&rect_i, margin, margin / 2);
1455 }
1456
1457 data->bbox.xmin = margin;
1458 data->bbox.xmax = BLI_rcti_size_x(&rect_i) - margin;
1459 data->bbox.ymin = margin;
1460 data->bbox.ymax = BLI_rcti_size_y(&rect_i);
1461
1462 /* region bigger for shadow */
1463 region->winrct.xmin = rect_i.xmin - margin;
1464 region->winrct.xmax = rect_i.xmax + margin;
1465 region->winrct.ymin = rect_i.ymin - margin;
1466 region->winrct.ymax = rect_i.ymax + margin;
1467 }
1468
1469 /* Adds sub-window. */
1471
1472 /* notify change and redraw */
1473 ED_region_tag_redraw(region);
1474
1475 return region;
1476}
1477
1479
1480/* -------------------------------------------------------------------- */
1483
1485 bContext *C, ARegion *butregion, uiBut *but, uiButExtraOpIcon *extra_icon, bool is_label)
1486{
1487 wmWindow *win = CTX_wm_window(C);
1488 float init_position[2];
1489
1490 if (but->drawflag & UI_BUT_NO_TOOLTIP) {
1491 return nullptr;
1492 }
1493 std::unique_ptr<uiTooltipData> data = nullptr;
1494
1495 if (but->tip_custom_func) {
1497 }
1498
1499 if (data == nullptr) {
1500 data = ui_tooltip_data_from_tool(C, but, is_label);
1501 }
1502
1503 if (data == nullptr) {
1504 data = ui_tooltip_data_from_button_or_extra_icon(C, but, extra_icon, is_label);
1505 }
1506
1507 if (data == nullptr) {
1508 data = ui_tooltip_data_from_button_or_extra_icon(C, but, nullptr, is_label);
1509 }
1510
1511 if (data == nullptr) {
1512 return nullptr;
1513 }
1514
1515 const bool is_no_overlap = UI_but_has_tooltip_label(but) || UI_but_is_tool(but);
1516 rcti init_rect;
1517 if (is_no_overlap) {
1518 rctf overlap_rect_fl;
1519 init_position[0] = BLI_rctf_cent_x(&but->rect);
1520 init_position[1] = BLI_rctf_cent_y(&but->rect);
1521 if (butregion) {
1522 ui_block_to_window_fl(butregion, but->block, &init_position[0], &init_position[1]);
1523 ui_block_to_window_rctf(butregion, but->block, &overlap_rect_fl, &but->rect);
1524 }
1525 else {
1526 overlap_rect_fl = but->rect;
1527 }
1528 BLI_rcti_rctf_copy_round(&init_rect, &overlap_rect_fl);
1529 }
1530 else if (but->type == UI_BTYPE_LABEL && BLI_rctf_size_y(&but->rect) > UI_UNIT_Y) {
1531 init_position[0] = win->eventstate->xy[0];
1532 init_position[1] = win->eventstate->xy[1] - (UI_POPUP_MARGIN / 2);
1533 }
1534 else {
1535 init_position[0] = BLI_rctf_cent_x(&but->rect);
1536 init_position[1] = but->rect.ymin;
1537 if (butregion) {
1538 ui_block_to_window_fl(butregion, but->block, &init_position[0], &init_position[1]);
1539 init_position[0] = win->eventstate->xy[0];
1540 }
1541 init_position[1] -= (UI_POPUP_MARGIN / 2);
1542 }
1543
1545 C, std::move(data), init_position, is_no_overlap ? &init_rect : nullptr);
1546
1547 return region;
1548}
1549
1551{
1552 return UI_tooltip_create_from_button_or_extra_icon(C, butregion, but, nullptr, is_label);
1553}
1554
1556{
1557 wmWindow *win = CTX_wm_window(C);
1558 float init_position[2] = {float(win->eventstate->xy[0]), float(win->eventstate->xy[1])};
1559
1560 std::unique_ptr<uiTooltipData> data = ui_tooltip_data_from_gizmo(C, gz);
1561 if (data == nullptr) {
1562 return nullptr;
1563 }
1564
1565 /* TODO(@harley): Julian preferred that the gizmo callback return the 3D bounding box
1566 * which we then project to 2D here. Would make a nice improvement. */
1567 if (gz->type->screen_bounds_get) {
1568 rcti bounds;
1569 if (gz->type->screen_bounds_get(C, gz, &bounds)) {
1570 init_position[0] = bounds.xmin;
1571 init_position[1] = bounds.ymin;
1572 }
1573 }
1574
1575 return ui_tooltip_create_with_data(C, std::move(data), init_position, nullptr);
1576}
1577
1579{
1580 if (ima.filepath[0]) {
1581 char root[FILE_MAX];
1584 }
1585
1586 std::string image_type;
1587 switch (ima.source) {
1588 case IMA_SRC_FILE:
1589 image_type = TIP_("Single Image");
1590 break;
1591 case IMA_SRC_SEQUENCE:
1592 image_type = TIP_("Image Sequence");
1593 break;
1594 case IMA_SRC_MOVIE:
1595 image_type = TIP_("Movie");
1596 break;
1597 case IMA_SRC_GENERATED:
1598 image_type = TIP_("Generated");
1599 break;
1600 case IMA_SRC_VIEWER:
1601 image_type = TIP_("Viewer");
1602 break;
1603 case IMA_SRC_TILED:
1604 image_type = TIP_("UDIM Tiles");
1605 break;
1606 }
1608
1609 short w;
1610 short h;
1611 ImBuf *ibuf = BKE_image_preview(&ima, 200.0f * UI_SCALE_FAC, &w, &h);
1612
1613 if (ibuf) {
1615 data, fmt::format("{} \u00D7 {}", w, h), {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_NORMAL);
1616 }
1617
1618 if (BKE_image_has_anim(&ima)) {
1619 ImBufAnim *anim = static_cast<ImBufAnim *>(ima.anims.first);
1620 if (anim) {
1621 int duration = IMB_anim_get_duration(anim, IMB_TC_RECORD_RUN);
1623 data, fmt::format("Frames: {}", duration), {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_NORMAL);
1624 }
1625 }
1626
1629
1631 data, fmt::format(TIP_("Users: {}"), ima.id.us), {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_NORMAL);
1632
1633 if (ibuf) {
1634 uiTooltipImage image_data;
1635 image_data.width = int(ibuf->x);
1636 image_data.height = int(ibuf->y);
1637 image_data.ibuf = ibuf;
1638 image_data.border = true;
1640 image_data.premultiplied = true;
1643 UI_tooltip_image_field_add(data, image_data);
1644 IMB_freeImBuf(ibuf);
1645 }
1646}
1647
1649{
1650 if (clip.filepath[0]) {
1651 char root[FILE_MAX];
1654 }
1655
1656 std::string image_type;
1657 switch (clip.source) {
1658 case IMA_SRC_SEQUENCE:
1659 image_type = TIP_("Image Sequence");
1660 break;
1661 case IMA_SRC_MOVIE:
1662 image_type = TIP_("Movie");
1663 break;
1664 }
1666
1667 if (clip.anim) {
1668 ImBufAnim *anim = clip.anim;
1669
1671 fmt::format("{} \u00D7 {}",
1674 {},
1677
1679 data,
1680 fmt::format("Frames: {}", IMB_anim_get_duration(anim, IMB_TC_RECORD_RUN)),
1681 {},
1684
1685 ImBuf *ibuf = IMB_anim_previewframe(anim);
1686
1687 if (ibuf) {
1688 /* Resize. */
1689 float scale = float(200.0f * UI_SCALE_FAC) / float(std::max(ibuf->x, ibuf->y));
1690 IMB_scale(ibuf, scale * ibuf->x, scale * ibuf->y, IMBScaleFilter::Box, false);
1691 IMB_rect_from_float(ibuf);
1692
1693 uiTooltipImage image_data;
1694 image_data.width = int(ibuf->x);
1695 image_data.height = int(ibuf->y);
1696 image_data.ibuf = ibuf;
1697 image_data.border = true;
1699 image_data.premultiplied = true;
1702 UI_tooltip_image_field_add(data, image_data);
1703 IMB_freeImBuf(ibuf);
1704 }
1705 }
1706}
1707
1709{
1710 if (!font.filepath[0]) {
1711 /* Let's not bother with packed files _for now_.*/
1712 return;
1713 }
1714
1715 if (!BLI_exists(font.filepath)) {
1717 data, TIP_("File not found"), {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_ALERT);
1718 return;
1719 }
1720
1721 float color[4];
1722 const uiWidgetColors *theme = ui_tooltip_get_theme();
1724 ImBuf *ibuf = IMB_font_preview(font.filepath, 200 * UI_SCALE_FAC, color);
1725 if (ibuf) {
1726 uiTooltipImage image_data;
1727 image_data.width = ibuf->x;
1728 image_data.height = ibuf->y;
1729 image_data.ibuf = ibuf;
1730 image_data.border = false;
1732 image_data.premultiplied = false;
1733 image_data.text_color = true;
1734 UI_tooltip_image_field_add(data, image_data);
1735 IMB_freeImBuf(ibuf);
1736 }
1737}
1738
1739static std::unique_ptr<uiTooltipData> ui_tooltip_data_from_search_item_tooltip_data(ID *id)
1740{
1741 std::unique_ptr<uiTooltipData> data = std::make_unique<uiTooltipData>();
1742 const ID_Type type_id = GS(id->name);
1743
1745
1746 if (type_id == ID_IM) {
1747 ui_tooltip_from_image(*reinterpret_cast<Image *>(id), *data);
1748 }
1749 else if (type_id == ID_MC) {
1750 ui_tooltip_from_clip(*reinterpret_cast<MovieClip *>(id), *data);
1751 }
1752 else if (type_id == ID_VF) {
1753 ui_tooltip_from_vfont(*reinterpret_cast<VFont *>(id), *data);
1754 }
1755 else {
1757 fmt::format(TIP_("Choose {} data-block to be assigned to this user"),
1759 {},
1762 }
1763
1765 if (ID_IS_LINKED(id)) {
1767 *data,
1768 fmt::format(TIP_("Source library: {}\n{}"), id->lib->id.name + 2, id->lib->filepath),
1769 {},
1772 }
1773
1774 return data->fields.is_empty() ? nullptr : std::move(data);
1775}
1776
1778 const ARegion *searchbox_region,
1779 const rcti *item_rect,
1780 ID *id)
1781{
1782 std::unique_ptr<uiTooltipData> data = ui_tooltip_data_from_search_item_tooltip_data(id);
1783 if (data == nullptr) {
1784 return nullptr;
1785 }
1786
1787 const wmWindow *win = CTX_wm_window(C);
1788 float init_position[2];
1789 init_position[0] = win->eventstate->xy[0];
1790 init_position[1] = item_rect->ymin + searchbox_region->winrct.ymin - (UI_POPUP_MARGIN / 2);
1791
1792 return ui_tooltip_create_with_data(C, std::move(data), init_position, nullptr);
1793}
1794
1795void UI_tooltip_free(bContext *C, bScreen *screen, ARegion *region)
1796{
1797 ui_region_temp_remove(C, screen, region);
1798}
1799
void immDrawPixelsTexScaledFullSize(const IMMDrawPixelsTexState *state, float x, float y, int img_w, int img_h, eGPUTextureFormat gpu_format, bool use_filter, const void *rect, float scaleX, float scaleY, float xzoom, float yzoom, const float color[4])
Definition glutil.cc:55
IMMDrawPixelsTexState immDrawPixelsTexSetup(int builtin)
Definition glutil.cc:40
bScreen * CTX_wm_screen(const bContext *C)
void CTX_wm_operator_poll_msg_clear(bContext *C)
ScrArea * CTX_wm_area(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
SpaceLink * CTX_wm_space_data(const bContext *C)
const char * CTX_wm_operator_poll_msg_get(bContext *C, bool *r_free)
const char * BKE_idtype_idcode_to_name(short idcode)
Definition idtype.cc:168
bool BKE_image_has_anim(Image *image)
ImBuf * BKE_image_preview(Image *ima, short max_size, short *r_width, short *r_height)
@ BLF_WORD_WRAP
Definition BLF_api.hh:367
void BLF_size(int fontid, float size)
Definition blf.cc:426
void BLF_disable(int fontid, int option)
Definition blf.cc:321
int blf_mono_font
Definition blf.cc:51
void BLF_enable(int fontid, int option)
Definition blf.cc:312
float BLF_width(int fontid, const char *str, size_t str_len, ResultBLF *r_info=nullptr) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(2)
Definition blf.cc:791
int BLF_height_max(int fontid) ATTR_WARN_UNUSED_RESULT
Definition blf.cc:828
void BLF_wordwrap(int fontid, int wrap_width)
Definition blf.cc:893
#define BLI_STATIC_ASSERT(a, msg)
Definition BLI_assert.h:87
#define BLI_assert(a)
Definition BLI_assert.h:50
File and directory operations.
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:350
#define LISTBASE_FOREACH(type, var, list)
MINLINE float max_ff(float a, float b)
MINLINE int min_ii(int a, int b)
MINLINE int max_ii(int a, int b)
void hsv_to_rgb_v(const float hsv[3], float r_rgb[3])
Definition math_color.cc:57
void rgb_to_hsv_v(const float rgb[3], float r_hsv[3])
MINLINE float rgb_to_grayscale(const float rgb[3])
void rgba_uchar_to_float(float r_col[4], const unsigned char col_ub[4])
void rgba_float_to_uchar(unsigned char r_col[4], const float col_f[4])
void rgb_uchar_to_float(float r_col[3], const unsigned char col_ub[3])
void rgb_float_to_uchar(unsigned char r_col[3], const float col_f[3])
MINLINE void copy_v3_v3(float r[3], const float a[3])
#define FILE_MAX
void void BLI_path_split_dir_part(const char *filepath, char *dir, size_t dir_maxncpy) ATTR_NONNULL(1
BLI_INLINE int BLI_rcti_size_y(const struct rcti *rct)
Definition BLI_rect.h:193
BLI_INLINE float BLI_rctf_cent_y(const struct rctf *rct)
Definition BLI_rect.h:184
BLI_INLINE float BLI_rctf_cent_x(const struct rctf *rct)
Definition BLI_rect.h:180
void BLI_rcti_rctf_copy_round(struct rcti *dst, const struct rctf *src)
void BLI_rcti_translate(struct rcti *rect, int x, int y)
Definition rct.c:560
void BLI_rcti_resize(struct rcti *rect, int x, int y)
Definition rct.c:615
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition BLI_rect.h:189
BLI_INLINE int BLI_rcti_cent_y(const struct rcti *rct)
Definition BLI_rect.h:176
void BLI_rcti_rctf_copy(struct rcti *dst, const struct rctf *src)
bool BLI_rcti_inside_rcti(const rcti *rct_a, const rcti *rct_b)
Definition rct.c:198
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition BLI_rect.h:201
bool BLI_rcti_clamp(struct rcti *rect, const struct rcti *rect_bounds, int r_xy[2])
BLI_INLINE int BLI_rcti_cent_x(const struct rcti *rct)
Definition BLI_rect.h:172
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.c:40
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:597
unsigned char uchar
unsigned int uint
#define ARRAY_SIZE(arr)
#define UNUSED_VARS(...)
#define ELEM(...)
#define STREQ(a, b)
#define TIP_(msgid)
#define CTX_IFACE_(context, msgid)
#define BLT_I18NCONTEXT_OPERATOR_DEFAULT
#define IFACE_(msgid)
#define CTX_TIP_(context, msgid)
bool bool BPY_run_string_as_intptr(bContext *C, const char *imports[], const char *expr, BPy_RunErrInfo *err_info, intptr_t *r_value) ATTR_NONNULL(1
bool bool bool BPY_run_string_as_string_and_len(bContext *C, const char *imports[], const char *expr, BPy_RunErrInfo *err_info, char **r_value, size_t *r_value_len) ATTR_NONNULL(1
bool bool bool bool BPY_run_string_as_string(bContext *C, const char *imports[], const char *expr, BPy_RunErrInfo *err_info, char **r_value) ATTR_NONNULL(1
#define ID_IS_LINKED(_id)
Definition DNA_ID.h:654
ID_Type
@ ID_MC
@ ID_IM
@ ID_VF
#define MAX_NAME
Definition DNA_defs.h:50
@ IMA_SRC_FILE
@ IMA_SRC_MOVIE
@ IMA_SRC_GENERATED
@ IMA_SRC_VIEWER
@ IMA_SRC_TILED
@ IMA_SRC_SEQUENCE
@ RGN_TYPE_TEMPORARY
@ USER_TOOLTIPS_PYTHON
#define UI_SCALE_FAC
void ED_region_floating_init(ARegion *region)
Definition area.cc:2210
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:634
void immUnbindProgram()
void immBindBuiltinProgram(eGPUBuiltinShader shader_id)
GPUVertFormat * immVertexFormat()
void immUniformColor4fv(const float rgba[4])
void imm_draw_box_wire_2d(uint pos, float x1, float y1, float x2, float y2)
void imm_draw_box_checker_2d(float x1, float y1, float x2, float y2)
void imm_draw_box_checker_2d_ex(float x1, float y1, float x2, float y2, const float color_primary[4], const float color_secondary[4], int checker_size)
@ GPU_SHADER_3D_UNIFORM_COLOR
@ GPU_SHADER_3D_IMAGE_COLOR
@ GPU_BLEND_NONE
Definition GPU_state.hh:85
@ GPU_BLEND_ALPHA
Definition GPU_state.hh:87
@ GPU_BLEND_ALPHA_PREMULT
Definition GPU_state.hh:88
void GPU_blend(eGPUBlend blend)
Definition gpu_state.cc:42
@ GPU_FETCH_FLOAT
uint GPU_vertformat_attr_add(GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_F32
int IMB_anim_get_image_height(ImBufAnim *anim)
ImBuf * IMB_dupImBuf(const ImBuf *ibuf1)
void IMB_rect_from_float(ImBuf *ibuf)
Definition divers.cc:694
void IMB_rectfill_area(ImBuf *ibuf, const float col[4], int x1, int y1, int x2, int y2, ColorManagedDisplay *display)
Definition rectop.cc:1241
int IMB_anim_get_image_width(ImBufAnim *anim)
ImBuf * IMB_anim_previewframe(ImBufAnim *anim)
int IMB_anim_get_duration(ImBufAnim *anim, IMB_Timecode_Type tc)
bool IMB_scale(ImBuf *ibuf, unsigned int newx, unsigned int newy, IMBScaleFilter filter, bool threaded=true)
Definition scaling.cc:779
@ IMB_TC_RECORD_RUN
Contains defines and structs used throughout the imbuf module.
@ IB_rect
ImBuf * IMB_font_preview(const char *filepath, unsigned int width, const float color[4])
Read Guarded memory(de)allocation.
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Brightness Control the brightness and contrast of the input color Vector Map input vector components with curves Camera Retrieve information about the camera and how it relates to the current shading point s position Clamp a value between a minimum and a maximum Vector Perform vector math operation Invert Invert a color
@ PROP_FLOAT
Definition RNA_types.hh:67
@ PROP_UNIT_ROTATION
Definition RNA_types.hh:81
@ PROP_ENUM_FLAG
Definition RNA_types.hh:293
@ PROP_PASSWORD
Definition RNA_types.hh:146
#define C
Definition RandGen.cpp:29
#define UI_ALPHA_CHECKER_LIGHT
#define UI_UNIT_Y
std::string UI_but_string_get_property_keymap(bContext &C, uiBut &but)
int UI_but_unit_type_get(const uiBut *but)
@ UI_BUT_NO_TOOLTIP
#define UI_ALPHA_CHECKER_DARK
std::optional< EnumPropertyItem > UI_but_rna_enum_item_get(bContext &C, uiBut &but)
PointerRNA * UI_but_extra_operator_icon_opptr_get(const uiButExtraOpIcon *extra_icon)
std::string UI_but_string_get_operator_keymap(bContext &C, uiBut &but)
std::string UI_but_string_get_label(uiBut &but)
@ UI_STYLE_TEXT_LEFT
const uiStyle * UI_style_get()
void UI_fontstyle_set(const uiFontStyle *fs)
@ UI_BUT_DISABLED
@ UI_BUT_DRIVEN
PointerRNA * UI_but_operator_ptr_ensure(uiBut *but)
uiTooltipStyle
@ UI_TIP_STYLE_IMAGE
@ UI_TIP_STYLE_MONO
@ UI_TIP_STYLE_NORMAL
@ UI_TIP_STYLE_SPACER
@ UI_TIP_STYLE_HEADER
std::string UI_but_string_get_rna_property_identifier(const uiBut &but)
std::string UI_but_extra_icon_string_get_tooltip(bContext &C, const uiButExtraOpIcon &extra_icon)
wmOperatorType * UI_but_extra_operator_icon_optype_get(const uiButExtraOpIcon *extra_icon)
@ UI_BLOCK_SHOW_SHORTCUT_ALWAYS
void UI_fontstyle_draw(const uiFontStyle *fs, const rcti *rect, const char *str, size_t str_len, const uchar col[4], const uiFontStyleDraw_Params *fs_params)
std::string UI_but_extra_icon_string_get_label(const uiButExtraOpIcon &extra_icon)
uiTooltipColorID
@ UI_TIP_LC_ALERT
@ UI_TIP_LC_PYTHON
@ UI_TIP_LC_MAIN
@ UI_TIP_LC_VALUE
@ UI_TIP_LC_NORMAL
@ UI_TIP_LC_ACTIVE
@ UI_TIP_LC_MAX
std::string UI_but_string_get_tooltip_label(const uiBut &but)
std::string UI_but_string_get_tooltip(bContext &C, uiBut &but)
std::string UI_but_string_get_rna_struct_identifier(const uiBut &but)
@ UI_BTYPE_TEXT
@ UI_BTYPE_LABEL
@ UI_BTYPE_ROW
@ UI_BTYPE_SEARCH_MENU
@ UI_BTYPE_COLOR
std::string UI_but_extra_icon_string_get_operator_keymap(const bContext &C, const uiButExtraOpIcon &extra_icon)
bool UI_but_is_tool(const uiBut *but)
bool UI_but_has_tooltip_label(const uiBut *but)
@ TH_BACK
void UI_GetThemeColor4fv(int colorid, float col[4])
wmOperatorCallContext
Definition WM_types.hh:216
@ WM_OP_INVOKE_REGION_WIN
Definition WM_types.hh:219
@ WM_OP_INVOKE_DEFAULT
Definition WM_types.hh:218
@ KM_SHIFT
Definition WM_types.hh:255
int pad[32 - sizeof(int)]
#define U
ATTR_WARN_UNUSED_RESULT const BMVert * v
static btDbvtVolume bounds(btDbvtNode **leaves, int count)
Definition btDbvt.cpp:299
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
static constexpr int64_t not_found
constexpr int64_t find(char c, int64_t pos=0) const
constexpr bool is_empty() const
constexpr bool startswith(StringRef prefix) const
constexpr const char & back() const
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
RAYTRACE_GROUP_SIZE additional_info("eevee_shared", "eevee_gbuffer_data", "eevee_global_ubo", "eevee_sampling_data", "eevee_utility_texture", "eevee_hiz_data", "draw_view") .specialization_constant(Type RAYTRACE_GROUP_SIZE in_sh_0_tx in_sh_2_tx screen_normal_tx GPU_RGBA8
#define str(s)
uint col
struct ImBuf * IMB_allocImBuf(unsigned int, unsigned int, unsigned char, unsigned int)
void IMB_freeImBuf(ImBuf *)
bool ui_but_context_poll_operator_ex(bContext *C, const uiBut *but, const wmOperatorCallParams *optype_params)
void ui_but_string_get(uiBut *but, char *str, const size_t str_maxncpy)
void ui_block_cm_to_display_space_v3(uiBlock *block, float pixel[3])
void ui_block_to_window_rctf(const ARegion *region, const uiBlock *block, rctf *rct_dst, const rctf *rct_src)
Definition interface.cc:163
void ui_but_v3_get(uiBut *but, float vec[3])
ColorManagedDisplay * ui_block_cm_display_get(uiBlock *block)
void ui_block_to_window_fl(const ARegion *region, const uiBlock *block, float *x, float *y)
Definition interface.cc:135
bool ui_but_anim_expression_get(uiBut *but, char *str, size_t str_maxncpy)
void ui_draw_tooltip_background(const uiStyle *style, uiBlock *block, const rcti *rect)
bool ui_block_is_pie_menu(const uiBlock *block) ATTR_WARN_UNUSED_RESULT
const uiWidgetColors * ui_tooltip_get_theme()
#define UI_POPUP_MARGIN
bool ui_block_is_menu(const uiBlock *block) ATTR_WARN_UNUSED_RESULT
bool ui_but_is_color_gamma(uiBut *but)
#define UI_TIP_PADDING
ARegion * UI_tooltip_create_from_gizmo(bContext *C, wmGizmo *gz)
#define TIP_BORDER_Y
#define UI_TIP_MAXWIDTH
static void rgb_tint(float col[3], float h, float h_strength, float v, float v_strength)
static std::string ui_tooltip_text_python_from_op(bContext *C, wmOperatorType *ot, PointerRNA *opptr)
ARegion * UI_tooltip_create_from_button_or_extra_icon(bContext *C, ARegion *butregion, uiBut *but, uiButExtraOpIcon *extra_icon, bool is_label)
static std::unique_ptr< uiTooltipData > ui_tooltip_data_from_custom_func(bContext *C, uiBut *but)
void UI_tooltip_free(bContext *C, bScreen *screen, ARegion *region)
void UI_tooltip_image_field_add(uiTooltipData &data, const uiTooltipImage &image_data)
static std::unique_ptr< uiTooltipData > ui_tooltip_data_from_search_item_tooltip_data(ID *id)
static std::unique_ptr< uiTooltipData > ui_tooltip_data_from_gizmo(bContext *C, wmGizmo *gz)
static void ui_tooltip_from_image(Image &ima, uiTooltipData &data)
static void ui_tooltip_from_clip(MovieClip &clip, uiTooltipData &data)
#define UI_TIP_SPACER
static bool ui_tooltip_period_needed(blender::StringRef tip)
#define TIP_BORDER_X
static void ui_tooltip_region_draw_cb(const bContext *, ARegion *region)
static ARegion * ui_tooltip_create_with_data(bContext *C, std::unique_ptr< uiTooltipData > data_uptr, const float init_position[2], const rcti *init_rect_overlap)
static void ui_tooltip_from_vfont(const VFont &font, uiTooltipData &data)
static std::unique_ptr< uiTooltipData > ui_tooltip_data_from_button_or_extra_icon(bContext *C, uiBut *but, uiButExtraOpIcon *extra_icon, const bool is_label)
static std::unique_ptr< uiTooltipData > ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is_label)
ARegion * UI_tooltip_create_from_button(bContext *C, ARegion *butregion, uiBut *but, bool is_label)
ARegion * UI_tooltip_create_from_search_item_generic(bContext *C, const ARegion *searchbox_region, const rcti *item_rect, ID *id)
static void ui_tooltip_region_free_cb(ARegion *region)
void UI_tooltip_text_field_add(uiTooltipData &data, std::string text, std::string suffix, const uiTooltipStyle style, const uiTooltipColorID color_id, const bool is_pad)
ARegion * ui_region_temp_add(bScreen *screen)
void ui_region_temp_remove(bContext *C, bScreen *screen, ARegion *region)
#define GS(x)
Definition iris.cc:202
format
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
static ulong state[N]
VecBase< int32_t, 2 > int2
float RNA_property_float_get(PointerRNA *ptr, PropertyRNA *prop)
const char * RNA_property_ui_description(const PropertyRNA *prop)
bool RNA_property_array_check(PropertyRNA *prop)
void RNA_string_set(PointerRNA *ptr, const char *name, const char *value)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
float RNA_property_float_get_index(PointerRNA *ptr, PropertyRNA *prop, int index)
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop)
PropertyType RNA_property_type(PropertyRNA *prop)
void RNA_string_get(PointerRNA *ptr, const char *name, char *value)
int RNA_property_flag(PropertyRNA *prop)
int RNA_property_array_length(PointerRNA *ptr, PropertyRNA *prop)
int RNA_property_enum_get(PointerRNA *ptr, PropertyRNA *prop)
PropertySubType RNA_property_subtype(PropertyRNA *prop)
std::optional< std::string > RNA_path_full_property_py_ex(const PointerRNA *ptr, PropertyRNA *prop, int index, bool use_fallback)
Definition rna_path.cc:1276
std::optional< std::string > RNA_path_full_struct_py(const PointerRNA *ptr)
Definition rna_path.cc:1260
_W64 int intptr_t
Definition stdint.h:118
void(* free)(ARegion *)
void(* draw)(const bContext *C, ARegion *region)
void * regiondata
struct ARegionType * type
Definition DNA_ID.h:413
struct Library * lib
Definition DNA_ID.h:419
int us
Definition DNA_ID.h:435
char name[66]
Definition DNA_ID.h:425
ListBase anims
ColorManagedColorspaceSettings colorspace_settings
char filepath[1024]
short source
char filepath[1024]
Definition DNA_ID.h:531
ID id
Definition DNA_ID.h:529
void * first
struct ImBufAnim * anim
char filepath[1024]
ID * owner_id
Definition RNA_types.hh:40
void * data
Definition RNA_types.hh:42
int lines
Definition BLF_api.hh:406
int width
Definition BLF_api.hh:410
char filepath[1024]
float xmax
float xmin
float ymax
float ymin
int ymin
int ymax
int xmin
int xmax
wmOperatorCallParams * optype_params
wmOperatorCallContext opcontext
PropertyRNA * rnaprop
wmOperatorType * optype
eButType type
const char * disabled_info
void * tip_arg
uiBlock * block
PointerRNA * opptr
uiButToolTipFunc tip_func
std::string drawstr
PointerRNA rnapoin
uiButToolTipCustomFunc tip_custom_func
uiFontStyle tooltip
blender::Vector< uiTooltipField > fields
struct uiTooltipField::@077121355254051061300053172025011134110030323203 geom
std::optional< uiTooltipImage > image
uiTooltipImageBackground background
unsigned char inner[4]
unsigned char text[4]
int xy[2]
Definition WM_types.hh:726
wmOperatorType * type
PropertyRNA * prop
wmGizmoFnScreenBoundsGet screen_bounds_get
int target_property_defs_len
const wmGizmoType * type
wmOperatorType * optype
Definition WM_types.hh:1118
wmOperatorCallContext opcontext
Definition WM_types.hh:1120
const char * idname
Definition WM_types.hh:992
struct wmEvent * eventstate
wmOperatorType * ot
Definition wm_files.cc:4125
wmGizmoOpElem * WM_gizmo_operator_get(wmGizmo *gz, int part_index)
Definition wm_gizmo.cc:195
wmGizmoProperty * WM_gizmo_target_property_array(wmGizmo *gz)
std::optional< std::string > WM_key_event_operator_string(const bContext *C, const char *opname, wmOperatorCallContext opcontext, IDProperty *properties, const bool is_strict)
std::optional< std::string > WM_keymap_item_to_string(const wmKeyMapItem *kmi, const bool compact)
std::string WM_operatortype_description_or_name(bContext *C, wmOperatorType *ot, PointerRNA *properties)
wmOperatorType * WM_operatortype_find(const char *idname, bool quiet)
std::string WM_operator_pystring_ex(bContext *C, wmOperator *op, const bool all_args, const bool macro_args, wmOperatorType *ot, PointerRNA *opptr)
void WM_operator_properties_create_ptr(PointerRNA *ptr, wmOperatorType *ot)
void WM_operator_properties_free(PointerRNA *ptr)
void WM_operator_properties_sanitize(PointerRNA *ptr, const bool no_context)
std::string WM_operator_pystring_abbreviate(std::string str, int str_len_max)
void wmOrtho2_region_pixelspace(const ARegion *region)
blender::int2 WM_window_native_pixel_size(const wmWindow *win)