Blender V4.5
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
29
30#include "MEM_guardedalloc.h"
31
32#include "DNA_userdef_types.h"
33
34#include "BLI_fileops.h"
35#include "BLI_listbase.h"
36#include "BLI_math_color.h"
37#include "BLI_math_vector.h"
38#include "BLI_path_utils.hh"
39#include "BLI_rect.h"
40#include "BLI_string.h"
41#include "BLI_utildefines.h"
42
43#include "BKE_context.hh"
44#include "BKE_idtype.hh"
45#include "BKE_image.hh"
46#include "BKE_library.hh"
47#include "BKE_main.hh"
48#include "BKE_paint.hh"
49#include "BKE_path_templates.hh"
50#include "BKE_screen.hh"
51#include "BKE_vfont.hh"
52
53#include "BIF_glutil.hh"
54
55#include "DNA_vfont_types.h"
56
57#include "GPU_immediate.hh"
58#include "GPU_immediate_util.hh"
59#include "GPU_state.hh"
60
61#include "IMB_imbuf.hh"
62#include "IMB_imbuf_types.hh"
63#include "IMB_thumbs.hh"
64
65#include "MOV_read.hh"
66
67#include "WM_api.hh"
68#include "WM_types.hh"
69
70#include "RNA_access.hh"
71#include "RNA_path.hh"
72#include "RNA_prototypes.hh"
73
74#include "UI_interface.hh"
75
76#include "BLF_api.hh"
77#include "BLT_translation.hh"
78
79#ifdef WITH_PYTHON
80# include "BPY_extern_run.hh"
81#endif
82
83#include "ED_screen.hh"
84
85#include "interface_intern.hh"
87
88/* Portions of line height. */
89#define UI_TIP_SPACER 0.3f
90#define UI_TIP_PADDING_X 1.95f
91#define UI_TIP_PADDING_Y 1.28f
92
93#define UI_TIP_MAXWIDTH 600
94
99
101 std::string text;
102 std::string text_suffix;
103 struct {
110 std::optional<uiTooltipImage> image;
111};
112
120
121BLI_STATIC_ASSERT(int(UI_TIP_LC_MAX) == int(UI_TIP_LC_ALERT) + 1, "invalid lc-max");
122
124 std::string text,
125 std::string suffix,
126 const uiTooltipStyle style,
127 const uiTooltipColorID color_id,
128 const bool is_pad)
129{
130 if (is_pad) {
131 /* Add a spacer field before this one. */
133 }
134
135 uiTooltipField field{};
136 field.format.style = style;
137 field.format.color_id = color_id;
138 field.text = std::move(text);
139 field.text_suffix = std::move(suffix);
140 data.fields.append(std::move(field));
141}
142
144{
145 uiTooltipField field{};
147 field.image = image_data;
148 field.image->ibuf = IMB_dupImBuf(image_data.ibuf);
149 data.fields.append(std::move(field));
150}
151
152/* -------------------------------------------------------------------- */
155
156static void color_blend_f3_f3(float dest[3], const float source[3], const float fac)
157{
158 if (fac != 0.0f) {
159 dest[0] = (1.0f - fac) * dest[0] + (fac * source[0]);
160 dest[1] = (1.0f - fac) * dest[1] + (fac * source[1]);
161 dest[2] = (1.0f - fac) * dest[2] + (fac * source[2]);
162 }
163}
164
165static void ui_tooltip_region_draw_cb(const bContext * /*C*/, ARegion *region)
166{
167 uiTooltipData *data = static_cast<uiTooltipData *>(region->regiondata);
168 const float pad_x = data->lineh * UI_TIP_PADDING_X;
169 const float pad_y = data->lineh * UI_TIP_PADDING_Y;
170 const uiWidgetColors *theme = ui_tooltip_get_theme();
171 rcti bbox = data->bbox;
172 float tip_colors[UI_TIP_LC_MAX][3];
173 uchar drawcol[4] = {0, 0, 0, 255}; /* to store color in while drawing (alpha is always 255) */
174
175 /* The color from the theme. */
176 float *main_color = tip_colors[UI_TIP_LC_MAIN];
177 float *value_color = tip_colors[UI_TIP_LC_VALUE];
178 float *active_color = tip_colors[UI_TIP_LC_ACTIVE];
179 float *normal_color = tip_colors[UI_TIP_LC_NORMAL];
180 float *python_color = tip_colors[UI_TIP_LC_PYTHON];
181 float *alert_color = tip_colors[UI_TIP_LC_ALERT];
182
183 float background_color[3];
184
186
187 /* Draw background. */
188 ui_draw_tooltip_background(UI_style_get(), nullptr, &bbox);
189
190 /* set background_color */
191 rgb_uchar_to_float(background_color, theme->inner);
192
193 /* `normal_color` is just tooltip text color. */
194 rgb_uchar_to_float(main_color, theme->text);
195 copy_v3_v3(normal_color, main_color);
196
197 /* `value_color` mixes with some background for less strength. */
198 copy_v3_v3(value_color, main_color);
199 color_blend_f3_f3(value_color, background_color, 0.2f);
200
201 /* `python_color` mixes with more background to be even dimmer. */
202 copy_v3_v3(python_color, main_color);
203 color_blend_f3_f3(python_color, background_color, 0.5f);
204
205 /* `active_color` is a light blue, push a bit toward text color. */
206 active_color[0] = 0.4f;
207 active_color[1] = 0.55f;
208 active_color[2] = 0.75f;
209 color_blend_f3_f3(active_color, main_color, 0.3f);
210
211 /* `alert_color` is red, push a bit toward text color. */
212 UI_GetThemeColor3fv(TH_REDALERT, alert_color);
213 color_blend_f3_f3(alert_color, main_color, 0.3f);
214
215 /* Draw text. */
216
217 /* Wrap most text typographically with hard width limit. */
218 BLF_wordwrap(data->fstyle.uifont_id,
219 data->wrap_width,
221
222 /* Wrap paths with path-specific wrapping with hard width limit. */
224 data->wrap_width,
226
227 bbox.xmin += 0.5f * pad_x; /* add padding to the text */
228 bbox.ymax -= 0.5f * pad_y;
229 bbox.ymax -= BLF_descender(data->fstyle.uifont_id);
230
231 for (int i = 0; i < data->fields.size(); i++) {
232 const uiTooltipField *field = &data->fields[i];
233
234 bbox.ymin = bbox.ymax - (data->lineh * field->geom.lines);
235 if (field->format.style == UI_TIP_STYLE_HEADER) {
236 uiFontStyleDraw_Params fs_params{};
237 fs_params.align = UI_STYLE_TEXT_LEFT;
238 fs_params.word_wrap = true;
239
240 /* Draw header and active data (is done here to be able to change color). */
241 rgb_float_to_uchar(drawcol, tip_colors[UI_TIP_LC_MAIN]);
242 UI_fontstyle_set(&data->fstyle);
244 &data->fstyle, &bbox, field->text.c_str(), field->text.size(), drawcol, &fs_params);
245
246 /* Offset to the end of the last line. */
247 if (!field->text_suffix.empty()) {
248 const float xofs = field->geom.x_pos;
249 const float yofs = data->lineh * (field->geom.lines - 1);
250 bbox.xmin += xofs;
251 bbox.ymax -= yofs;
252
253 rgb_float_to_uchar(drawcol, tip_colors[UI_TIP_LC_ACTIVE]);
254 UI_fontstyle_draw(&data->fstyle,
255 &bbox,
256 field->text_suffix.c_str(),
257 field->text_suffix.size(),
258 drawcol,
259 &fs_params);
260
261 /* Undo offset. */
262 bbox.xmin -= xofs;
263 bbox.ymax += yofs;
264 }
265 }
266 else if (field->format.style == UI_TIP_STYLE_MONO) {
267 uiFontStyleDraw_Params fs_params{};
268 fs_params.align = UI_STYLE_TEXT_LEFT;
269 fs_params.word_wrap = true;
270 uiFontStyle fstyle_mono = data->fstyle;
271 fstyle_mono.uifont_id = blf_mono_font;
272
273 UI_fontstyle_set(&fstyle_mono);
274 /* XXX: needed because we don't have mono in 'U.uifonts'. */
275 BLF_size(fstyle_mono.uifont_id, fstyle_mono.points * UI_SCALE_FAC);
276 rgb_float_to_uchar(drawcol, tip_colors[int(field->format.color_id)]);
278 &fstyle_mono, &bbox, field->text.c_str(), field->text.size(), drawcol, &fs_params);
279 }
280 else if (field->format.style == UI_TIP_STYLE_IMAGE && field->image.has_value()) {
281
282 bbox.ymax -= field->image->height;
283
284 if (field->image->background == uiTooltipImageBackground::Checkerboard_Themed) {
285 imm_draw_box_checker_2d(float(bbox.xmin),
286 float(bbox.ymax),
287 float(bbox.xmin + field->image->width),
288 float(bbox.ymax + field->image->height));
289 }
290 else if (field->image->background == uiTooltipImageBackground::Checkerboard_Fixed) {
291 const float checker_dark = UI_ALPHA_CHECKER_DARK / 255.0f;
292 const float checker_light = UI_ALPHA_CHECKER_LIGHT / 255.0f;
293 const float color1[4] = {checker_dark, checker_dark, checker_dark, 1.0f};
294 const float color2[4] = {checker_light, checker_light, checker_light, 1.0f};
295 imm_draw_box_checker_2d_ex(float(bbox.xmin + U.pixelsize),
296 float(bbox.ymax + U.pixelsize),
297 float(bbox.xmin + field->image->width),
298 float(bbox.ymax + field->image->height),
299 color1,
300 color2,
301 8);
302 }
303
304 GPU_blend((field->image->premultiplied) ? GPU_BLEND_ALPHA_PREMULT : GPU_BLEND_ALPHA);
305
308 bbox.xmin,
309 bbox.ymax,
310 field->image->ibuf->x,
311 field->image->ibuf->y,
312 GPU_RGBA8,
313 true,
314 field->image->ibuf->byte_buffer.data,
315 1.0f,
316 1.0f,
317 float(field->image->width) / float(field->image->ibuf->x),
318 float(field->image->height) / float(field->image->ibuf->y),
319 (field->image->text_color) ? main_color : nullptr);
320
321 if (field->image->border) {
326 float border_color[4] = {1.0f, 1.0f, 1.0f, 0.15f};
327 float bgcolor[4];
329 if (srgb_to_grayscale(bgcolor) > 0.5f) {
330 border_color[0] = 0.0f;
331 border_color[1] = 0.0f;
332 border_color[2] = 0.0f;
333 }
334 immUniformColor4fv(border_color);
336 float(bbox.xmin),
337 float(bbox.ymax),
338 float(bbox.xmin + field->image->width),
339 float(bbox.ymax + field->image->height));
342 }
343 }
344 else if (field->format.style == UI_TIP_STYLE_SPACER) {
345 bbox.ymax -= data->lineh * UI_TIP_SPACER;
346 }
347 else {
349 uiFontStyleDraw_Params fs_params{};
350 fs_params.align = UI_STYLE_TEXT_LEFT;
351 fs_params.word_wrap = true;
352
353 /* Draw remaining data. */
354 rgb_float_to_uchar(drawcol, tip_colors[int(field->format.color_id)]);
355 UI_fontstyle_set(&data->fstyle);
357 &data->fstyle, &bbox, field->text.c_str(), field->text.size(), drawcol, &fs_params);
358 }
359
360 bbox.ymax -= data->lineh * field->geom.lines;
361 }
362
363 BLF_disable(data->fstyle.uifont_id, BLF_WORD_WRAP);
365}
366
368{
369 /* Put ownership back into a unique pointer. */
370 std::unique_ptr<uiTooltipData> data{static_cast<uiTooltipData *>(region->regiondata)};
371 for (uiTooltipField &field : data->fields) {
372 if (field.image && field.image->ibuf) {
373 IMB_freeImBuf(field.image->ibuf);
374 }
375 }
376 region->regiondata = nullptr;
377}
378
380
381/* -------------------------------------------------------------------- */
384
387 PointerRNA *opptr)
388{
389 std::string str = WM_operator_pystring_ex(C, nullptr, false, false, ot, opptr);
390
391 /* Avoid overly verbose tips (eg, arrays of 20 layers), exact limit is arbitrary. */
392 return WM_operator_pystring_abbreviate(std::move(str), 32);
393}
394
396
397/* -------------------------------------------------------------------- */
400
401#ifdef WITH_PYTHON
402
403static bool ui_tooltip_data_append_from_keymap(bContext *C, uiTooltipData &data, wmKeyMap *keymap)
404{
405 const int fields_len_init = data.fields.size();
406
407 LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) {
408 wmOperatorType *ot = WM_operatortype_find(kmi->idname, true);
409 if (!ot) {
410 continue;
411 }
412 /* Tip. */
414 ot->description ? ot->description : ot->name,
415 {},
418 true);
419
420 /* Shortcut. */
421 const std::string kmi_str = WM_keymap_item_to_string(kmi, false).value_or("None");
423 fmt::format(fmt::runtime(TIP_("Shortcut: {}")), kmi_str),
424 {},
427
428 /* Python. */
429 if (U.flag & USER_TOOLTIPS_PYTHON) {
430 std::string str = ui_tooltip_text_python_from_op(C, ot, kmi->ptr);
432 fmt::format(fmt::runtime(TIP_("Python: {}")), str),
433 {},
436 }
437 }
438
439 return (fields_len_init != data.fields.size());
440}
441
442#endif /* WITH_PYTHON */
443
445{
446 if (tip.is_empty()) {
447 return false;
448 }
449
450 /* Already ends with punctuation. */
451 if (ELEM(tip.back(), '.', '!', '?')) {
452 return false;
453 }
454
455 /* Contains a bullet Unicode character. */
456 if (tip.find("\xe2\x80\xa2") != blender::StringRef::not_found) {
457 return false;
458 }
459
460 return true;
461}
462
466static std::unique_ptr<uiTooltipData> ui_tooltip_data_from_tool(bContext *C,
467 uiBut *but,
468 bool is_quick_tip)
469{
470 if (but->optype == nullptr) {
471 return nullptr;
472 }
473 /* While this should always be set for buttons as they are shown in the UI,
474 * the operator search popup can create a button that has no properties, see: #112541. */
475 if (but->opptr == nullptr) {
476 return nullptr;
477 }
478
479 if (!STREQ(but->optype->idname, "WM_OT_tool_set_by_id")) {
480 return nullptr;
481 }
482
483 /* Needed to get the space-data's type (below). */
484 if (CTX_wm_space_data(C) == nullptr) {
485 return nullptr;
486 }
487
488 char tool_id[MAX_NAME];
489 RNA_string_get(but->opptr, "name", tool_id);
490 BLI_assert(tool_id[0] != '\0');
491
492 /* When false, we're in a different space type to the tool being set.
493 * Needed for setting the fallback tool from the properties space.
494 *
495 * If we drop the hard coded 3D-view in properties hack, we can remove this check. */
496 bool has_valid_context = true;
497 const char *has_valid_context_error = IFACE_("Unsupported context");
498 {
499 ScrArea *area = CTX_wm_area(C);
500 if (area == nullptr) {
501 has_valid_context = false;
502 }
503 else {
504 PropertyRNA *prop = RNA_struct_find_property(but->opptr, "space_type");
505 if (RNA_property_is_set(but->opptr, prop)) {
506 const int space_type_prop = RNA_property_enum_get(but->opptr, prop);
507 if (space_type_prop != area->spacetype) {
508 has_valid_context = false;
509 }
510 }
511 }
512 }
513
514 /* We have a tool, now extract the info. */
515 std::unique_ptr<uiTooltipData> data = std::make_unique<uiTooltipData>();
516
517#ifdef WITH_PYTHON
518 /* It turns out to be most simple to do this via Python since C
519 * doesn't have access to information about non-active tools. */
520
521 /* Title (when icon-only). */
522 if (but->drawstr.empty()) {
523 const char *expr_imports[] = {"bpy", "bl_ui", nullptr};
524 char expr[256];
525 SNPRINTF(expr,
526 "bl_ui.space_toolsystem_common.item_from_id("
527 "bpy.context, "
528 "bpy.context.space_data.type, "
529 "'%s').label",
530 tool_id);
531 char *expr_result = nullptr;
532 bool is_error = false;
533
534 if (has_valid_context == false) {
535 expr_result = BLI_strdup(has_valid_context_error);
536 }
537 else if (BPY_run_string_as_string(C, expr_imports, expr, nullptr, &expr_result)) {
538 if (STREQ(expr_result, "")) {
539 MEM_freeN(expr_result);
540 expr_result = nullptr;
541 }
542 }
543 else {
544 /* NOTE: this is an exceptional case, we could even remove it
545 * however there have been reports of tooltips failing, so keep it for now. */
546 expr_result = BLI_strdup(IFACE_("Internal error!"));
547 is_error = true;
548 }
549
550 if (expr_result != nullptr) {
551 /* NOTE: This is a very weak hack to get a valid translation most of the time...
552 * Proper way to do would be to get i18n context from the item, somehow. */
553 const char *label_str = CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, expr_result);
554 if (label_str == expr_result) {
555 label_str = IFACE_(expr_result);
556 }
557
558 if (label_str != expr_result) {
559 MEM_freeN(expr_result);
560 expr_result = BLI_strdup(label_str);
561 }
562
564 expr_result,
565 {},
567 (is_error) ? UI_TIP_LC_ALERT : UI_TIP_LC_MAIN,
568 false);
569 MEM_freeN(expr_result);
570 }
571 }
572
573 /* Tip. */
574 if (is_quick_tip == false) {
575 const char *expr_imports[] = {"bpy", "bl_ui", nullptr};
576 char expr[256];
577 SNPRINTF(expr,
578 "bl_ui.space_toolsystem_common.description_from_id("
579 "bpy.context, "
580 "bpy.context.space_data.type, "
581 "'%s')",
582 tool_id);
583
584 char *expr_result = nullptr;
585 bool is_error = false;
586
587 if (has_valid_context == false) {
588 expr_result = BLI_strdup(has_valid_context_error);
589 }
590 else if (BPY_run_string_as_string(C, expr_imports, expr, nullptr, &expr_result)) {
591 if (STREQ(expr_result, "")) {
592 MEM_freeN(expr_result);
593 expr_result = nullptr;
594 }
595 }
596 else {
597 /* NOTE: this is an exceptional case, we could even remove it
598 * however there have been reports of tooltips failing, so keep it for now. */
599 expr_result = BLI_strdup(TIP_("Internal error!"));
600 is_error = true;
601 }
602
603 if (expr_result != nullptr) {
604 const bool add_period = ui_tooltip_period_needed(expr_result);
606 fmt::format("{}{}", expr_result, add_period ? "." : ""),
607 {},
609 (is_error) ? UI_TIP_LC_ALERT : UI_TIP_LC_MAIN,
610 false);
611 MEM_freeN(expr_result);
612 }
613 }
614
615 /* Shortcut. */
616 const bool show_shortcut = is_quick_tip == false &&
618
619 if (show_shortcut) {
620 /* There are different kinds of shortcuts:
621 *
622 * - Direct access to the tool (as if the toolbar button is pressed).
623 * - The key is assigned to the operator itself
624 * (bypassing the tool, executing the operator).
625 *
626 * Either way case it's useful to show the shortcut.
627 */
628 std::string shortcut = UI_but_string_get_operator_keymap(*C, *but);
629
630 if (shortcut.empty()) {
631 /* Check for direct access to the tool. */
632 if (std::optional<std::string> shortcut_toolbar = WM_key_event_operator_string(
633 C, "WM_OT_toolbar", WM_OP_INVOKE_REGION_WIN, nullptr, true))
634 {
635 /* Generate keymap in order to inspect it.
636 * NOTE: we could make a utility to avoid the keymap generation part of this. */
637 const char *expr_imports[] = {
638 "bpy", "bl_keymap_utils", "bl_keymap_utils.keymap_from_toolbar", nullptr};
639 const char *expr =
640 ("getattr("
641 "bl_keymap_utils.keymap_from_toolbar.generate("
642 "bpy.context, "
643 "bpy.context.space_data.type), "
644 "'as_pointer', lambda: 0)()");
645
646 intptr_t expr_result = 0;
647
648 if (has_valid_context == false) {
649 shortcut = has_valid_context_error;
650 }
651 else if (BPY_run_string_as_intptr(C, expr_imports, expr, nullptr, &expr_result)) {
652 if (expr_result != 0) {
653 wmKeyMap *keymap = (wmKeyMap *)expr_result;
654 LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) {
655 if (STREQ(kmi->idname, but->optype->idname)) {
656 char tool_id_test[MAX_NAME];
657 RNA_string_get(kmi->ptr, "name", tool_id_test);
658 if (STREQ(tool_id, tool_id_test)) {
659 std::string kmi_str = WM_keymap_item_to_string(kmi, false).value_or("");
660 shortcut = fmt::format("{}, {}", *shortcut_toolbar, kmi_str);
661 break;
662 }
663 }
664 }
665 }
666 }
667 else {
668 BLI_assert(0);
669 }
670 }
671 }
672
673 if (!shortcut.empty()) {
675 fmt::format(fmt::runtime(TIP_("Shortcut: {}")), shortcut),
676 {},
679 true);
680 }
681 }
682
683 if (show_shortcut) {
684 /* Shortcut for Cycling
685 *
686 * As a second option, we may have a shortcut to cycle this tool group.
687 *
688 * Since some keymaps may use this for the primary means of binding keys,
689 * it's useful to show these too.
690 * Without this there is no way to know how to use a key to set the tool.
691 *
692 * This is a little involved since the shortcut may be bound to another tool in this group,
693 * instead of the current tool on display. */
694
695 char *expr_result = nullptr;
696 size_t expr_result_len;
697
698 {
699 const char *expr_imports[] = {"bpy", "bl_ui", nullptr};
700 char expr[256];
701 SNPRINTF(expr,
702 "'\\x00'.join("
703 "item.idname for item in bl_ui.space_toolsystem_common.item_group_from_id("
704 "bpy.context, "
705 "bpy.context.space_data.type, '%s', coerce=True) "
706 "if item is not None)",
707 tool_id);
708
709 if (has_valid_context == false) {
710 /* pass */
711 }
713 C, expr_imports, expr, nullptr, &expr_result, &expr_result_len))
714 {
715 /* pass. */
716 }
717 }
718
719 if (expr_result != nullptr) {
720 PointerRNA op_props;
722 RNA_boolean_set(&op_props, "cycle", true);
723
724 std::optional<std::string> shortcut;
725
726 const char *item_end = expr_result + expr_result_len;
727 const char *item_step = expr_result;
728
729 while (item_step < item_end) {
730 RNA_string_set(&op_props, "name", item_step);
732 but->optype->idname,
734 static_cast<IDProperty *>(op_props.data),
735 true);
736 if (shortcut) {
737 break;
738 }
739 item_step += strlen(item_step) + 1;
740 }
741
743 MEM_freeN(expr_result);
744
745 if (shortcut) {
747 fmt::format(fmt::runtime(TIP_("Shortcut Cycle: {}")), *shortcut),
748 {},
751 true);
752 }
753 }
754 }
755
756 /* Python */
757 if ((is_quick_tip == false) && (U.flag & USER_TOOLTIPS_PYTHON)) {
758 std::string str = ui_tooltip_text_python_from_op(C, but->optype, but->opptr);
760 fmt::format(fmt::runtime(TIP_("Python: {}")), str),
761 {},
764 true);
765 }
766
767 /* Keymap */
768
769 /* This is too handy not to expose somehow, let's be sneaky for now. */
770 if ((is_quick_tip == false) && CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) {
771 const char *expr_imports[] = {"bpy", "bl_ui", nullptr};
772 char expr[256];
773 SNPRINTF(expr,
774 "getattr("
775 "bl_ui.space_toolsystem_common.keymap_from_id("
776 "bpy.context, "
777 "bpy.context.space_data.type, "
778 "'%s'), "
779 "'as_pointer', lambda: 0)()",
780 tool_id);
781
782 intptr_t expr_result = 0;
783
784 if (has_valid_context == false) {
785 /* pass */
786 }
787 else if (BPY_run_string_as_intptr(C, expr_imports, expr, nullptr, &expr_result)) {
788 if (expr_result != 0) {
790 *data, TIP_("Tool Keymap:"), {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_NORMAL, true);
791 wmKeyMap *keymap = (wmKeyMap *)expr_result;
792 ui_tooltip_data_append_from_keymap(C, *data, keymap);
793 }
794 }
795 else {
796 BLI_assert(0);
797 }
798 }
799#else
800 UNUSED_VARS(is_label, has_valid_context, has_valid_context_error);
801#endif /* WITH_PYTHON */
802
803 return data->fields.is_empty() ? nullptr : std::move(data);
804}
805
806static std::string ui_tooltip_color_string(const blender::float4 &color,
807 const blender::StringRefNull title,
808 const bool show_alpha,
809 const bool show_hex = false)
810{
811 if (show_hex) {
812 uchar hex[4];
813 rgba_float_to_uchar(hex, color);
814 if (show_alpha) {
815 return fmt::format("{}: #{:02X}{:02X}{:02X}{:02X}",
816 TIP_(title),
817 int(hex[0]),
818 int(hex[1]),
819 int(hex[2]),
820 int(hex[3]));
821 }
822 return fmt::format(
823 "{}: #{:02X}{:02X}{:02X}", TIP_(title), int(hex[0]), int(hex[1]), int(hex[2]));
824 }
825
826 if (show_alpha) {
827 return fmt::format("{}: {:.3f} {:.3f} {:.3f} {:.3f}",
828 TIP_(title),
829 color[0],
830 color[1],
831 color[2],
832 color[3]);
833 }
834
835 return fmt::format("{}: {:.3f} {:.3f} {:.3f}", TIP_(title), color[0], color[1], color[2]);
836};
837
838static std::unique_ptr<uiTooltipData> ui_tooltip_data_from_button_or_extra_icon(
839 bContext *C, uiBut *but, uiButExtraOpIcon *extra_icon, const bool is_quick_tip)
840{
841 char buf[512];
842
843 wmOperatorType *optype = extra_icon ? UI_but_extra_operator_icon_optype_get(extra_icon) :
844 but->optype;
845 PropertyRNA *rnaprop = extra_icon ? nullptr : but->rnaprop;
846
847 std::unique_ptr<uiTooltipData> data = std::make_unique<uiTooltipData>();
848
849 /* Menus already show shortcuts, don't show them in the tool-tips. */
850 const bool is_menu = ui_block_is_menu(but->block) && !ui_block_is_pie_menu(but->block);
851
852 std::string but_label;
853 std::string but_tip;
854 std::string but_tip_label;
855 std::string op_keymap;
856 std::string prop_keymap;
857 std::string rna_struct;
858 std::string rna_prop;
859 std::string enum_label;
860 std::string enum_tip;
861
862 if (extra_icon) {
863 if (is_quick_tip) {
864 but_label = UI_but_extra_icon_string_get_label(*extra_icon);
865 }
866 else {
867 but_label = UI_but_extra_icon_string_get_label(*extra_icon);
868 but_tip = UI_but_extra_icon_string_get_tooltip(*C, *extra_icon);
869 if (!is_menu) {
870 op_keymap = UI_but_extra_icon_string_get_operator_keymap(*C, *extra_icon);
871 }
872 }
873 }
874 else {
875 const std::optional<EnumPropertyItem> enum_item = UI_but_rna_enum_item_get(*C, *but);
876 if (is_quick_tip) {
877 but_tip_label = UI_but_string_get_tooltip_label(*but);
878 but_label = UI_but_string_get_label(*but);
879 enum_label = enum_item ? enum_item->name : "";
880 }
881 else {
882 but_label = UI_but_string_get_label(*but);
883 but_tip_label = UI_but_string_get_tooltip_label(*but);
884 but_tip = UI_but_string_get_tooltip(*C, *but);
885 enum_label = enum_item ? enum_item->name : "";
886 const char *description_c = enum_item ? enum_item->description : nullptr;
887 enum_tip = description_c ? description_c : "";
888 if (!is_menu) {
889 op_keymap = UI_but_string_get_operator_keymap(*C, *but);
890 prop_keymap = UI_but_string_get_property_keymap(*C, *but);
891 }
894 }
895 }
896
897 /* Label: If there is a custom tooltip label, use that to override the label to display.
898 * Otherwise fallback to the regular label. */
899 if (!but_tip_label.empty()) {
902 }
903 /* Regular (non-custom) label. Only show when the button doesn't already show the label. Check
904 * prefix instead of comparing because the button may include the shortcut. Buttons with dynamic
905 * tool-tips also don't get their default label here since they can already provide more accurate
906 * and specific tool-tip content. */
907 else if (!but_label.empty() && !blender::StringRef(but->drawstr).startswith(but_label) &&
908 !but->tip_func)
909 {
910 if (!enum_label.empty()) {
912 fmt::format("{}: ", but_label),
913 enum_label,
916 }
917 else {
919 }
921 }
922
923 /* Tip */
924 if (!but_tip.empty()) {
925 if (!enum_label.empty() && enum_label == but_label) {
927 *data, fmt::format("{}: ", but_tip), enum_label, UI_TIP_STYLE_HEADER, UI_TIP_LC_NORMAL);
929 }
930 else {
931 const bool add_period = ui_tooltip_period_needed(but_tip);
933 fmt::format("{}{}", but_tip, add_period ? "." : ""),
934 {},
937 if (but_label.empty()) {
939 }
940 }
941
942 /* special case enum rna buttons */
943 if ((but->type & UI_BTYPE_ROW) && rnaprop && RNA_property_flag(rnaprop) & PROP_ENUM_FLAG) {
945 TIP_("(Shift-Click/Drag to select multiple)"),
946 {},
949 }
950 }
951 /* When there is only an enum label (no button label or tip), draw that as header. */
952 else if (!enum_label.empty() && but_label.empty()) {
954 *data, std::move(enum_label), {}, UI_TIP_STYLE_HEADER, UI_TIP_LC_NORMAL);
955 }
956
957 /* Don't include further details if this is just a quick label tooltip. */
958 if (is_quick_tip) {
959 return data->fields.is_empty() ? nullptr : std::move(data);
960 }
961
962 /* Enum field label & tip. */
963 if (!enum_tip.empty()) {
965 *data, std::move(enum_tip), {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_VALUE);
966 }
967
968 /* Operator shortcut. */
969 if (!op_keymap.empty()) {
971 fmt::format(fmt::runtime(TIP_("Shortcut: {}")), op_keymap),
972 {},
975 !data->fields.is_empty());
976 }
977
978 /* Property context-toggle shortcut. */
979 if (!prop_keymap.empty()) {
981 fmt::format(fmt::runtime(TIP_("Shortcut: {}")), prop_keymap),
982 {},
985 true);
986 }
987
989 /* Better not show the value of a password. */
990 if ((rnaprop && (RNA_property_subtype(rnaprop) == PROP_PASSWORD)) == 0) {
991 /* Full string. */
992 ui_but_string_get(but, buf, sizeof(buf));
993 if (buf[0]) {
995 fmt::format(fmt::runtime(TIP_("Value: {}")), buf),
996 {},
999 true);
1000 }
1001 }
1002 }
1003
1004 if (rnaprop) {
1005 const int unit_type = UI_but_unit_type_get(but);
1006
1007 if (unit_type == PROP_UNIT_ROTATION) {
1008 if (RNA_property_type(rnaprop) == PROP_FLOAT) {
1009 float value = RNA_property_array_check(rnaprop) ?
1010 RNA_property_float_get_index(&but->rnapoin, rnaprop, but->rnaindex) :
1011 RNA_property_float_get(&but->rnapoin, rnaprop);
1013 fmt::format(fmt::runtime(TIP_("Radians: {}")), value),
1014 {},
1017 }
1018 }
1019
1020 if (but->flag & UI_BUT_DRIVEN) {
1021 if (ui_but_anim_expression_get(but, buf, sizeof(buf))) {
1023 fmt::format(fmt::runtime(TIP_("Expression: {}")), buf),
1024 {},
1027 }
1028 }
1029
1030 if (but->rnapoin.owner_id) {
1031 const ID *id = but->rnapoin.owner_id;
1032 if (ID_IS_LINKED(id)) {
1034 const bool is_builtin = BLI_path_contains(assets_path.c_str(), id->lib->filepath);
1035 const blender::StringRef title = is_builtin ? TIP_("Built-in Asset") : TIP_("Library");
1036 const blender::StringRef lib_path = id->lib->filepath;
1037 const blender::StringRef path = is_builtin ? lib_path.substr(assets_path.size()) :
1038 id->lib->filepath;
1040 *data, fmt::format("{}: {}", title, path), {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_NORMAL);
1041 }
1042 }
1043 }
1044
1045 /* Warn on path validity errors. */
1046 if (ELEM(but->type, UI_BTYPE_TEXT) &&
1047 /* Check red-alert, if the flag is not set, then this was suppressed. */
1048 (but->flag & UI_BUT_REDALERT))
1049 {
1050 if (rnaprop) {
1051 PropertySubType subtype = RNA_property_subtype(rnaprop);
1052
1053 /* If relative paths are used when unsupported (will already display red-alert). */
1054 if (ELEM(subtype, PROP_FILEPATH, PROP_DIRPATH)) {
1056 if (BLI_path_is_rel(but->drawstr.c_str())) {
1058 "Warning: the blend-file relative path prefix \"//\" "
1059 "is not supported for this property.",
1060 {},
1063 }
1064 }
1065 }
1066
1067 /* We include PROP_NONE here because some plain string properties are used
1068 * as parts of paths. For example, the sub-paths in the compositor's File
1069 * Output node. */
1070 if (ELEM(subtype, PROP_FILEPATH, PROP_DIRPATH, PROP_NONE)) {
1071 /* Template parse errors, for paths that support it. */
1072 if ((RNA_property_flag(rnaprop) & PROP_PATH_SUPPORTS_TEMPLATES) != 0) {
1073 const std::string path = RNA_property_string_get(&but->rnapoin, rnaprop);
1075 const std::optional<blender::bke::path_templates::VariableMap> variables =
1077 BLI_assert(variables.has_value());
1078
1080 BKE_path_validate_template(path, *variables);
1081
1082 if (!errors.is_empty()) {
1083 std::string error_message("Path template error(s):");
1084 for (const blender::bke::path_templates::Error &error : errors) {
1085 error_message += "\n - " + BKE_path_template_error_to_string(error, path);
1086 }
1088 *data, error_message, {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_ALERT);
1089 }
1090 }
1091 }
1092 }
1093 }
1094 }
1095
1096 /* Button is disabled, we may be able to tell user why. */
1097 if ((but->flag & UI_BUT_DISABLED) || extra_icon) {
1098 const char *disabled_msg_orig = nullptr;
1099 const char *disabled_msg = nullptr;
1100 bool disabled_msg_free = false;
1101
1102 /* If operator poll check failed, it can give pretty precise info why. */
1103 if (optype) {
1104 const wmOperatorCallContext opcontext = extra_icon ? extra_icon->optype_params->opcontext :
1105 but->opcontext;
1106 wmOperatorCallParams call_params{};
1107 call_params.optype = optype;
1108 call_params.opcontext = opcontext;
1110 ui_but_context_poll_operator_ex(C, but, &call_params);
1111 disabled_msg_orig = CTX_wm_operator_poll_msg_get(C, &disabled_msg_free);
1112 disabled_msg = TIP_(disabled_msg_orig);
1113 }
1114 /* Alternatively, buttons can store some reasoning too. */
1115 else if (!extra_icon && but->disabled_info) {
1116 disabled_msg = TIP_(but->disabled_info);
1117 }
1118
1119 if (disabled_msg && disabled_msg[0]) {
1121 fmt::format(fmt::runtime(TIP_("Disabled: {}")), disabled_msg),
1122 {},
1125 }
1126 if (disabled_msg_free) {
1127 MEM_freeN(disabled_msg_orig);
1128 }
1129 }
1130
1131 if (U.flag & USER_TOOLTIPS_PYTHON && optype && !rnaprop) {
1132 PointerRNA *opptr = extra_icon ? UI_but_extra_operator_icon_opptr_get(extra_icon) :
1133 /* Allocated when needed, the button owns it. */
1135
1136 /* So the context is passed to field functions (some Python field functions use it). */
1137 WM_operator_properties_sanitize(opptr, false);
1138
1139 std::string str = ui_tooltip_text_python_from_op(C, optype, opptr);
1140
1141 /* Operator info. */
1143 fmt::format(fmt::runtime(TIP_("Python: {}")), str),
1144 {},
1147 true);
1148 }
1149
1150 if ((U.flag & USER_TOOLTIPS_PYTHON) && !optype && !rna_struct.empty()) {
1151 {
1153 *data,
1154 rna_prop.empty() ?
1155 fmt::format(fmt::runtime(TIP_("Python: {}")), rna_struct) :
1156 fmt::format(fmt::runtime(TIP_("Python: {}.{}")), rna_struct, rna_prop),
1157 {},
1160 (data->fields.size() > 0));
1161 }
1162
1163 if (but->rnapoin.owner_id) {
1164 std::optional<std::string> str = rnaprop ? RNA_path_full_property_py_ex(
1165 &but->rnapoin, rnaprop, but->rnaindex, true) :
1168 }
1169 }
1170
1171 if (but->type == UI_BTYPE_COLOR) {
1172
1173 float color[4];
1174 ui_but_v3_get(but, color);
1175 color[3] = 1.0f;
1176 bool has_alpha = false;
1177
1178 if (but->rnaprop) {
1179 BLI_assert(but->rnaindex == -1);
1180 has_alpha = RNA_property_array_length(&but->rnapoin, but->rnaprop) >= 4;
1181 if (has_alpha) {
1182 color[3] = RNA_property_float_get_index(&but->rnapoin, but->rnaprop, 3);
1183 }
1184 }
1185
1186 if (!ui_but_is_color_gamma(but)) {
1188 }
1189
1190 const std::string hex_st = ui_tooltip_color_string(color, "Hex", has_alpha, true);
1191
1192 const std::string rgba_st = ui_tooltip_color_string(
1193 color, has_alpha ? "RGBA" : "RGB", has_alpha);
1194
1195 float hsva[4];
1196 rgb_to_hsv_v(color, hsva);
1197 hsva[3] = color[3];
1198 const std::string hsva_st = ui_tooltip_color_string(
1199 hsva, has_alpha ? "HSVA" : "HSV", has_alpha);
1200
1201 const uiFontStyle *fs = &UI_style_get()->tooltip;
1203 float w = BLF_width(blf_mono_font, hsva_st.c_str(), hsva_st.size());
1204
1205 uiTooltipImage image_data;
1206 image_data.width = int(w);
1207 image_data.height = int(w / (has_alpha ? 4.0f : 3.0f));
1208 image_data.ibuf = IMB_allocImBuf(image_data.width, image_data.height, 32, IB_byte_data);
1209 image_data.border = true;
1210 image_data.premultiplied = false;
1211
1212 const ColorManagedDisplay *display = ui_block_cm_display_get(but->block);
1213 if (color[3] == 1.0f) {
1214 /* No transparency so draw the entire area solid without checkerboard. */
1217 image_data.ibuf, color, 1, 1, image_data.width, image_data.height, display);
1218 }
1219 else {
1221 /* Draw one half with transparency. */
1222 IMB_rectfill_area(image_data.ibuf,
1223 color,
1224 image_data.width / 2,
1225 1,
1226 image_data.width,
1227 image_data.height,
1228 display);
1229 /* Draw the other half with a solid color. */
1230 color[3] = 1.0f;
1232 image_data.ibuf, color, 1, 1, image_data.width / 2, image_data.height, display);
1233 }
1234
1237 UI_tooltip_image_field_add(*data, image_data);
1243
1244 /* Tooltip now owns a copy of the ImBuf, so we can delete ours. */
1245 IMB_freeImBuf(image_data.ibuf);
1246 }
1247
1248 /* If the last field is a spacer, remove it. */
1249 while (!data->fields.is_empty() && data->fields.last().format.style == UI_TIP_STYLE_SPACER) {
1250 data->fields.pop_last();
1251 }
1252
1253 return data->fields.is_empty() ? nullptr : std::move(data);
1254}
1255
1256static std::unique_ptr<uiTooltipData> ui_tooltip_data_from_gizmo(bContext *C, wmGizmo *gz)
1257{
1258 std::unique_ptr<uiTooltipData> data = std::make_unique<uiTooltipData>();
1259
1260 /* TODO(@ideasman42): a way for gizmos to have their own descriptions (low priority). */
1261
1262 /* Operator Actions */
1263 {
1264 const bool use_drag = gz->drag_part != -1 && gz->highlight_part != gz->drag_part;
1265 struct GizmoOpActions {
1266 int part;
1267 const char *prefix;
1268 };
1269 GizmoOpActions gzop_actions[] = {
1270 {
1271 gz->highlight_part,
1272 use_drag ? CTX_TIP_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Click") : nullptr,
1273 },
1274 {
1275 use_drag ? gz->drag_part : -1,
1276 use_drag ? CTX_TIP_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Drag") : nullptr,
1277 },
1278 };
1279
1280 for (int i = 0; i < ARRAY_SIZE(gzop_actions); i++) {
1281 wmGizmoOpElem *gzop = (gzop_actions[i].part != -1) ?
1282 WM_gizmo_operator_get(gz, gzop_actions[i].part) :
1283 nullptr;
1284 if (gzop != nullptr) {
1285 /* Description */
1286 std::string info = WM_operatortype_description_or_name(C, gzop->type, &gzop->ptr);
1287
1288 if (!info.empty()) {
1290 *data,
1291 gzop_actions[i].prefix ? fmt::format("{}: {}", gzop_actions[i].prefix, info) : info,
1292 {},
1295 false);
1296 }
1297
1298 /* Shortcut */
1299 {
1300 IDProperty *prop = static_cast<IDProperty *>(gzop->ptr.data);
1301 if (std::optional<std::string> shortcut_str = WM_key_event_operator_string(
1302 C, gzop->type->idname, WM_OP_INVOKE_DEFAULT, prop, true))
1303 {
1305 *data,
1306 fmt::format(fmt::runtime(TIP_("Shortcut: {}")), *shortcut_str),
1307 {},
1310 true);
1311 }
1312 }
1313 }
1314 }
1315 }
1316
1317 /* Property Actions */
1318 for (const wmGizmoProperty &gz_prop : gz->target_properties) {
1319 if (gz_prop.prop != nullptr) {
1320 const char *info = RNA_property_ui_description(gz_prop.prop);
1321 if (info && info[0]) {
1323 }
1324 }
1325 }
1326
1327 return data->fields.is_empty() ? nullptr : std::move(data);
1328}
1329
1330static std::unique_ptr<uiTooltipData> ui_tooltip_data_from_custom_func(bContext *C, uiBut *but)
1331{
1332 /* Create tooltip data. */
1333 std::unique_ptr<uiTooltipData> data = std::make_unique<uiTooltipData>();
1334
1335 /* Create fields from custom callback. */
1336 but->tip_custom_func(*C, *data, but->tip_arg);
1337
1338 return data->fields.is_empty() ? nullptr : std::move(data);
1339}
1340
1342 std::unique_ptr<uiTooltipData> data_uptr,
1343 const float init_position[2],
1344 const rcti *init_rect_overlap)
1345{
1346 wmWindow *win = CTX_wm_window(C);
1347 const blender::int2 win_size = WM_window_native_pixel_size(win);
1348 rcti rect_i;
1349 int font_flag = 0;
1350
1351 /* Create area region. */
1353
1354 static ARegionType type;
1355 memset(&type, 0, sizeof(ARegionType));
1359 region->runtime->type = &type;
1360 /* Move ownership to region data. The region type free callback puts it back into a unique
1361 * pointer for save freeing. */
1362 region->regiondata = data_uptr.release();
1363
1364 uiTooltipData *data = static_cast<uiTooltipData *>(region->regiondata);
1365
1366 /* Set font, get bounding-box. */
1367 const uiStyle *style = UI_style_get();
1368 data->fstyle = style->tooltip; /* copy struct */
1369 BLF_size(data->fstyle.uifont_id, data->fstyle.points * UI_SCALE_FAC);
1370 int h = BLF_height_max(data->fstyle.uifont_id);
1371 const float pad_x = h * UI_TIP_PADDING_X;
1372 const float pad_y = h * UI_TIP_PADDING_Y;
1373
1374 UI_fontstyle_set(&data->fstyle);
1375
1376 data->wrap_width = min_ii(UI_TIP_MAXWIDTH * UI_SCALE_FAC, win_size[0] - pad_x);
1377
1378 font_flag |= BLF_WORD_WRAP;
1379 BLF_enable(data->fstyle.uifont_id, font_flag);
1380 BLF_enable(blf_mono_font, font_flag);
1381 BLF_wordwrap(data->fstyle.uifont_id,
1382 data->wrap_width,
1385 data->wrap_width,
1387
1388 int i, fonth, fontw;
1389 for (i = 0, fontw = 0, fonth = 0; i < data->fields.size(); i++) {
1390 uiTooltipField *field = &data->fields[i];
1391 ResultBLF info = {0};
1392 int w = 0;
1393 int x_pos = 0;
1394 int font_id;
1395
1396 if (field->format.style == UI_TIP_STYLE_MONO) {
1397 BLF_size(blf_mono_font, data->fstyle.points * UI_SCALE_FAC);
1398 font_id = blf_mono_font;
1399 }
1400 else {
1401 font_id = data->fstyle.uifont_id;
1402 }
1403
1404 if (!field->text.empty()) {
1405 w = BLF_width(font_id, field->text.c_str(), field->text.size(), &info);
1406 }
1407
1408 /* check for suffix (enum label) */
1409 if (!field->text_suffix.empty()) {
1410 x_pos = info.width;
1411 w = max_ii(w,
1412 x_pos + BLF_width(font_id, ": ", BLF_DRAW_STR_DUMMY_MAX) +
1413 BLF_width(font_id, field->text_suffix.c_str(), BLF_DRAW_STR_DUMMY_MAX));
1414 }
1415
1416 fonth += h * info.lines;
1417
1418 if (field->format.style == UI_TIP_STYLE_SPACER) {
1419 fonth += h * UI_TIP_SPACER;
1420 }
1421
1422 if (field->format.style == UI_TIP_STYLE_IMAGE && field->image) {
1423 fonth += field->image->height;
1424 w = max_ii(w, field->image->width);
1425 }
1426
1427 fontw = max_ii(fontw, w);
1428
1429 field->geom.lines = info.lines;
1430 field->geom.x_pos = x_pos;
1431 }
1432
1433 BLF_disable(data->fstyle.uifont_id, font_flag);
1434 BLF_disable(blf_mono_font, font_flag);
1435
1436 data->toth = fonth;
1437 data->lineh = h;
1438
1439 /* Compute position. */
1440 {
1441 rctf rect_fl;
1442 rect_fl.xmin = init_position[0] - (h * 0.2f) - (pad_x * 0.5f);
1443 rect_fl.xmax = rect_fl.xmin + fontw;
1444 rect_fl.ymax = init_position[1] - (h * 0.2f) - (pad_y * 0.5f);
1445 rect_fl.ymin = rect_fl.ymax - fonth;
1446 BLI_rcti_rctf_copy(&rect_i, &rect_fl);
1447 }
1448
1449 // #define USE_ALIGN_Y_CENTER
1450
1451 /* Clamp to window bounds. */
1452 {
1453 /* Ensure at least 5 pixels above screen bounds.
1454 * #UI_UNIT_Y is just a guess to be above the menu item. */
1455 if (init_rect_overlap != nullptr) {
1456 const int pad = max_ff(1.0f, U.pixelsize) * 5;
1457 rcti init_rect;
1458 init_rect.xmin = init_rect_overlap->xmin - pad;
1459 init_rect.xmax = init_rect_overlap->xmax + pad;
1460 init_rect.ymin = init_rect_overlap->ymin - pad;
1461 init_rect.ymax = init_rect_overlap->ymax + pad;
1462 rcti rect_clamp;
1463 rect_clamp.xmin = 0;
1464 rect_clamp.xmax = win_size[0];
1465 rect_clamp.ymin = 0;
1466 rect_clamp.ymax = win_size[1];
1467 /* try right. */
1468 const int size_x = BLI_rcti_size_x(&rect_i);
1469 const int size_y = BLI_rcti_size_y(&rect_i);
1470 const int cent_overlap_x = BLI_rcti_cent_x(&init_rect);
1471#ifdef USE_ALIGN_Y_CENTER
1472 const int cent_overlap_y = BLI_rcti_cent_y(&init_rect);
1473#endif
1474 struct {
1475 rcti xpos;
1476 rcti xneg;
1477 rcti ypos;
1478 rcti yneg;
1479 } rect;
1480
1481 { /* xpos */
1482 rcti r = rect_i;
1483 r.xmin = init_rect.xmax;
1484 r.xmax = r.xmin + size_x;
1485#ifdef USE_ALIGN_Y_CENTER
1486 r.ymin = cent_overlap_y - (size_y / 2);
1487 r.ymax = r.ymin + size_y;
1488#else
1489 r.ymin = init_rect.ymax - BLI_rcti_size_y(&rect_i);
1490 r.ymax = init_rect.ymax;
1491 r.ymin -= UI_POPUP_MARGIN;
1492 r.ymax -= UI_POPUP_MARGIN;
1493#endif
1494 rect.xpos = r;
1495 }
1496 { /* xneg */
1497 rcti r = rect_i;
1498 r.xmin = init_rect.xmin - size_x;
1499 r.xmax = r.xmin + size_x;
1500#ifdef USE_ALIGN_Y_CENTER
1501 r.ymin = cent_overlap_y - (size_y / 2);
1502 r.ymax = r.ymin + size_y;
1503#else
1504 r.ymin = init_rect.ymax - BLI_rcti_size_y(&rect_i);
1505 r.ymax = init_rect.ymax;
1506 r.ymin -= UI_POPUP_MARGIN;
1507 r.ymax -= UI_POPUP_MARGIN;
1508#endif
1509 rect.xneg = r;
1510 }
1511 { /* ypos */
1512 rcti r = rect_i;
1513 r.xmin = cent_overlap_x - (size_x / 2);
1514 r.xmax = r.xmin + size_x;
1515 r.ymin = init_rect.ymax;
1516 r.ymax = r.ymin + size_y;
1517 rect.ypos = r;
1518 }
1519 { /* yneg */
1520 rcti r = rect_i;
1521 r.xmin = cent_overlap_x - (size_x / 2);
1522 r.xmax = r.xmin + size_x;
1523 r.ymin = init_rect.ymin - size_y;
1524 r.ymax = r.ymin + size_y;
1525 rect.yneg = r;
1526 }
1527
1528 bool found = false;
1529 for (int j = 0; j < 4; j++) {
1530 const rcti *r = (&rect.xpos) + j;
1531 if (BLI_rcti_inside_rcti(&rect_clamp, r)) {
1532 rect_i = *r;
1533 found = true;
1534 break;
1535 }
1536 }
1537 if (!found) {
1538 /* Fallback, we could pick the best fallback, for now just use xpos. */
1539 int offset_dummy[2];
1540 rect_i = rect.xpos;
1541 BLI_rcti_clamp(&rect_i, &rect_clamp, offset_dummy);
1542 }
1543 }
1544 else {
1545 const int clamp_pad_x = int((5.0f * UI_SCALE_FAC) + (pad_x * 0.5f));
1546 const int clamp_pad_y = int((7.0f * UI_SCALE_FAC) + (pad_y * 0.5f));
1547 rcti rect_clamp;
1548 rect_clamp.xmin = clamp_pad_x;
1549 rect_clamp.xmax = win_size[0] - clamp_pad_x;
1550 rect_clamp.ymin = clamp_pad_y;
1551 rect_clamp.ymax = win_size[1] - clamp_pad_y;
1552 int offset_dummy[2];
1553 BLI_rcti_clamp(&rect_i, &rect_clamp, offset_dummy);
1554 }
1555 }
1556
1557#undef USE_ALIGN_Y_CENTER
1558
1559 /* add padding */
1560 BLI_rcti_resize(&rect_i, BLI_rcti_size_x(&rect_i) + pad_x, BLI_rcti_size_y(&rect_i) + pad_y);
1561
1562 /* widget rect, in region coords */
1563 {
1564 /* Compensate for margin offset, visually this corrects the position. */
1565 const int margin = UI_POPUP_MARGIN;
1566 if (init_rect_overlap != nullptr) {
1567 BLI_rcti_translate(&rect_i, margin, margin / 2);
1568 }
1569
1570 data->bbox.xmin = margin;
1571 data->bbox.xmax = BLI_rcti_size_x(&rect_i) + margin;
1572 data->bbox.ymin = margin;
1573 data->bbox.ymax = BLI_rcti_size_y(&rect_i) + margin;
1574
1575 /* region bigger for shadow */
1576 region->winrct.xmin = rect_i.xmin - margin;
1577 region->winrct.xmax = rect_i.xmax + margin;
1578 region->winrct.ymin = rect_i.ymin - margin;
1579 region->winrct.ymax = rect_i.ymax + margin;
1580 }
1581
1582 /* Adds sub-window. */
1584
1585 /* notify change and redraw */
1586 ED_region_tag_redraw(region);
1587
1588 return region;
1589}
1590
1592
1593/* -------------------------------------------------------------------- */
1596
1598 bContext *C, ARegion *butregion, uiBut *but, uiButExtraOpIcon *extra_icon, bool is_quick_tip)
1599{
1600 wmWindow *win = CTX_wm_window(C);
1601 float init_position[2];
1602
1603 if (but->drawflag & UI_BUT_NO_TOOLTIP) {
1604 return nullptr;
1605 }
1606 std::unique_ptr<uiTooltipData> data = nullptr;
1607
1608 if (!is_quick_tip && but->tip_custom_func) {
1610 }
1611
1612 if (data == nullptr) {
1613 data = ui_tooltip_data_from_tool(C, but, is_quick_tip);
1614 }
1615
1616 if (data == nullptr) {
1617 data = ui_tooltip_data_from_button_or_extra_icon(C, but, extra_icon, is_quick_tip);
1618 }
1619
1620 if (data == nullptr) {
1621 data = ui_tooltip_data_from_button_or_extra_icon(C, but, nullptr, is_quick_tip);
1622 }
1623
1624 if (data == nullptr) {
1625 return nullptr;
1626 }
1627
1628 const bool is_no_overlap = UI_but_has_quick_tooltip(but) || UI_but_is_tool(but);
1629 rcti init_rect;
1630 if (is_no_overlap) {
1631 rctf overlap_rect_fl;
1632 init_position[0] = BLI_rctf_cent_x(&but->rect);
1633 init_position[1] = BLI_rctf_cent_y(&but->rect);
1634 if (butregion) {
1635 ui_block_to_window_fl(butregion, but->block, &init_position[0], &init_position[1]);
1636 ui_block_to_window_rctf(butregion, but->block, &overlap_rect_fl, &but->rect);
1637 }
1638 else {
1639 overlap_rect_fl = but->rect;
1640 }
1641 BLI_rcti_rctf_copy_round(&init_rect, &overlap_rect_fl);
1642 }
1643 else if (but->type == UI_BTYPE_LABEL && BLI_rctf_size_y(&but->rect) > UI_UNIT_Y) {
1644 init_position[0] = win->eventstate->xy[0];
1645 init_position[1] = win->eventstate->xy[1] - (UI_POPUP_MARGIN / 2);
1646 }
1647 else {
1648 init_position[0] = BLI_rctf_cent_x(&but->rect);
1649 init_position[1] = but->rect.ymin;
1650 if (butregion) {
1651 ui_block_to_window_fl(butregion, but->block, &init_position[0], &init_position[1]);
1652 init_position[0] = win->eventstate->xy[0];
1653 }
1654 init_position[1] -= (UI_POPUP_MARGIN / 2);
1655 }
1656
1658 C, std::move(data), init_position, is_no_overlap ? &init_rect : nullptr);
1659
1660 return region;
1661}
1662
1664 ARegion *butregion,
1665 uiBut *but,
1666 bool is_quick_tip)
1667{
1668 return UI_tooltip_create_from_button_or_extra_icon(C, butregion, but, nullptr, is_quick_tip);
1669}
1670
1672{
1673 wmWindow *win = CTX_wm_window(C);
1674 float init_position[2] = {float(win->eventstate->xy[0]), float(win->eventstate->xy[1])};
1675
1676 std::unique_ptr<uiTooltipData> data = ui_tooltip_data_from_gizmo(C, gz);
1677 if (data == nullptr) {
1678 return nullptr;
1679 }
1680
1681 /* TODO(@harley): Julian preferred that the gizmo callback return the 3D bounding box
1682 * which we then project to 2D here. Would make a nice improvement. */
1683 if (gz->type->screen_bounds_get) {
1684 rcti bounds;
1685 if (gz->type->screen_bounds_get(C, gz, &bounds)) {
1686 init_position[0] = bounds.xmin;
1687 init_position[1] = bounds.ymin;
1688 }
1689 }
1690
1691 return ui_tooltip_create_with_data(C, std::move(data), init_position, nullptr);
1692}
1693
1695{
1696 if (ima.filepath[0]) {
1697 char root[FILE_MAX];
1700 }
1701
1702 std::string image_type;
1703 switch (ima.source) {
1704 case IMA_SRC_FILE:
1705 image_type = TIP_("Single Image");
1706 break;
1707 case IMA_SRC_SEQUENCE:
1708 image_type = TIP_("Image Sequence");
1709 break;
1710 case IMA_SRC_MOVIE:
1711 image_type = TIP_("Movie");
1712 break;
1713 case IMA_SRC_GENERATED:
1714 image_type = TIP_("Generated");
1715 break;
1716 case IMA_SRC_VIEWER:
1717 image_type = TIP_("Viewer");
1718 break;
1719 case IMA_SRC_TILED:
1720 image_type = TIP_("UDIM Tiles");
1721 break;
1722 }
1724
1725 short w;
1726 short h;
1727 ImBuf *ibuf = BKE_image_preview(&ima, 200.0f * UI_SCALE_FAC, &w, &h);
1728
1729 if (ibuf) {
1731 data, fmt::format("{} \u00D7 {}", w, h), {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_NORMAL);
1732 }
1733
1734 if (BKE_image_has_anim(&ima)) {
1735 MovieReader *anim = static_cast<ImageAnim *>(ima.anims.first)->anim;
1736 if (anim) {
1737 int duration = MOV_get_duration_frames(anim, IMB_TC_RECORD_RUN);
1739 data, fmt::format("Frames: {}", duration), {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_NORMAL);
1740 }
1741 }
1742
1745
1747 fmt::format(fmt::runtime(TIP_("Users: {}")), ima.id.us),
1748 {},
1751
1752 if (ibuf) {
1753 uiTooltipImage image_data;
1754 image_data.width = ibuf->x;
1755 image_data.height = ibuf->y;
1756 image_data.ibuf = ibuf;
1757 image_data.border = true;
1759 image_data.premultiplied = true;
1762 UI_tooltip_image_field_add(data, image_data);
1763 IMB_freeImBuf(ibuf);
1764 }
1765}
1766
1768{
1769 if (clip.filepath[0]) {
1770 char root[FILE_MAX];
1773 }
1774
1775 std::string image_type;
1776 switch (clip.source) {
1777 case IMA_SRC_SEQUENCE:
1778 image_type = TIP_("Image Sequence");
1779 break;
1780 case IMA_SRC_MOVIE:
1781 image_type = TIP_("Movie");
1782 break;
1783 }
1785
1786 if (clip.anim) {
1787 MovieReader *anim = clip.anim;
1788
1790 data,
1791 fmt::format("{} \u00D7 {}", MOV_get_image_width(anim), MOV_get_image_height(anim)),
1792 {},
1795
1797 data,
1798 fmt::format("Frames: {}", MOV_get_duration_frames(anim, IMB_TC_RECORD_RUN)),
1799 {},
1802
1803 ImBuf *ibuf = MOV_decode_preview_frame(anim);
1804
1805 if (ibuf) {
1806 /* Resize. */
1807 float scale = (200.0f * UI_SCALE_FAC) / float(std::max(ibuf->x, ibuf->y));
1808 IMB_scale(ibuf, scale * ibuf->x, scale * ibuf->y, IMBScaleFilter::Box, false);
1809 IMB_byte_from_float(ibuf);
1810
1811 uiTooltipImage image_data;
1812 image_data.width = ibuf->x;
1813 image_data.height = ibuf->y;
1814 image_data.ibuf = ibuf;
1815 image_data.border = true;
1817 image_data.premultiplied = true;
1820 UI_tooltip_image_field_add(data, image_data);
1821 IMB_freeImBuf(ibuf);
1822 }
1823 }
1824}
1825
1827{
1828 if (BKE_vfont_is_builtin(&font)) {
1829 /* In memory font previews are currently not supported,
1830 * don't attempt to handle as a file. */
1831 return;
1832 }
1833 if (!font.filepath[0]) {
1834 /* These may be packed files, currently not supported. */
1835 return;
1836 }
1837
1838 char filepath_abs[FILE_MAX];
1839 STRNCPY(filepath_abs, font.filepath);
1840 BLI_path_abs(filepath_abs, ID_BLEND_PATH_FROM_GLOBAL(&font.id));
1841
1842 if (!BLI_exists(filepath_abs)) {
1844 data, TIP_("File not found"), {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_ALERT);
1845 return;
1846 }
1847
1848 float color[4];
1849 const uiWidgetColors *theme = ui_tooltip_get_theme();
1850 rgba_uchar_to_float(color, theme->text);
1851 ImBuf *ibuf = IMB_font_preview(filepath_abs, 200 * UI_SCALE_FAC, color);
1852 if (ibuf) {
1853 uiTooltipImage image_data;
1854 image_data.width = ibuf->x;
1855 image_data.height = ibuf->y;
1856 image_data.ibuf = ibuf;
1857 image_data.border = false;
1859 image_data.premultiplied = false;
1860 image_data.text_color = true;
1861 UI_tooltip_image_field_add(data, image_data);
1862 IMB_freeImBuf(ibuf);
1863 }
1864}
1865
1866static std::unique_ptr<uiTooltipData> ui_tooltip_data_from_search_item_tooltip_data(ID *id)
1867{
1868 std::unique_ptr<uiTooltipData> data = std::make_unique<uiTooltipData>();
1869 const ID_Type type_id = GS(id->name);
1870
1872
1873 if (type_id == ID_IM) {
1874 ui_tooltip_from_image(*reinterpret_cast<Image *>(id), *data);
1875 }
1876 else if (type_id == ID_MC) {
1877 ui_tooltip_from_clip(*reinterpret_cast<MovieClip *>(id), *data);
1878 }
1879 else if (type_id == ID_VF) {
1880 ui_tooltip_from_vfont(*reinterpret_cast<VFont *>(id), *data);
1881 }
1882 else {
1884 *data,
1885 fmt::format(fmt::runtime(TIP_("Choose {} data-block to be assigned to this user")),
1887 {},
1890 }
1891
1893 if (ID_IS_LINKED(id)) {
1895 fmt::format(fmt::runtime(TIP_("Source library: {}\n{}")),
1896 id->lib->id.name + 2,
1897 id->lib->filepath),
1898 {},
1901 }
1902
1903 return data->fields.is_empty() ? nullptr : std::move(data);
1904}
1905
1907 const ARegion *searchbox_region,
1908 const rcti *item_rect,
1909 ID *id)
1910{
1911 std::unique_ptr<uiTooltipData> data = ui_tooltip_data_from_search_item_tooltip_data(id);
1912 if (data == nullptr) {
1913 return nullptr;
1914 }
1915
1916 const wmWindow *win = CTX_wm_window(C);
1917 float init_position[2];
1918 init_position[0] = win->eventstate->xy[0];
1919 init_position[1] = item_rect->ymin + searchbox_region->winrct.ymin - (UI_POPUP_MARGIN / 2);
1920
1921 return ui_tooltip_create_with_data(C, std::move(data), init_position, nullptr);
1922}
1923
1924void UI_tooltip_free(bContext *C, bScreen *screen, ARegion *region)
1925{
1926 ui_region_temp_remove(C, screen, region);
1927}
1928
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:50
IMMDrawPixelsTexState immDrawPixelsTexSetup(int builtin)
Definition glutil.cc:36
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:165
bool BKE_image_has_anim(Image *image)
ImBuf * BKE_image_preview(Image *ima, short max_size, short *r_width, short *r_height)
Functions and classes for applying templates with variable expressions to filepaths.
std::optional< blender::bke::path_templates::VariableMap > BKE_build_template_variables_for_prop(const bContext *C, PointerRNA *ptr, PropertyRNA *prop)
std::string BKE_path_template_error_to_string(const blender::bke::path_templates::Error &error, blender::StringRef path)
blender::Vector< blender::bke::path_templates::Error > BKE_path_validate_template(blender::StringRef path, const blender::bke::path_templates::VariableMap &template_variables)
bool BKE_path_contains_template_syntax(blender::StringRef path)
bool BKE_vfont_is_builtin(const VFont *vfont)
Definition vfont.cc:273
void BLF_size(int fontid, float size)
Definition blf.cc:440
BLFWrapMode
Definition BLF_api.hh:44
@ Typographical
Definition BLF_api.hh:46
int BLF_descender(int fontid) ATTR_WARN_UNUSED_RESULT
Definition blf.cc:875
@ BLF_WORD_WRAP
Definition BLF_api.hh:439
#define BLF_DRAW_STR_DUMMY_MAX
Definition BLF_api.hh:468
void BLF_disable(int fontid, int option)
Definition blf.cc:329
void BLF_wordwrap(int fontid, int wrap_width, BLFWrapMode mode=BLFWrapMode::Minimal)
Definition blf.cc:918
int blf_mono_font
Definition blf.cc:48
blender::ocio::Display ColorManagedDisplay
Definition BLF_api.hh:35
void BLF_enable(int fontid, int option)
Definition blf.cc:320
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:805
int BLF_height_max(int fontid) ATTR_WARN_UNUSED_RESULT
Definition blf.cc:853
#define BLI_STATIC_ASSERT(a, msg)
Definition BLI_assert.h:83
#define BLI_assert(a)
Definition BLI_assert.h:46
File and directory operations.
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:373
#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 rgb_to_hsv_v(const float rgb[3], float r_hsv[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 float srgb_to_grayscale(const float rgb[3])
MINLINE void copy_v3_v3(float r[3], const float a[3])
bool BLI_path_abs(char path[FILE_MAX], const char *basepath) ATTR_NONNULL(1
#define FILE_MAX
bool BLI_path_contains(const char *container_path, const char *containee_path) ATTR_NONNULL(1
bool BLI_path_is_rel(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
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:198
BLI_INLINE float BLI_rctf_cent_y(const struct rctf *rct)
Definition BLI_rect.h:189
BLI_INLINE float BLI_rctf_cent_x(const struct rctf *rct)
Definition BLI_rect.h:185
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.cc:566
void BLI_rcti_resize(struct rcti *rect, int x, int y)
Definition rct.cc:621
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition BLI_rect.h:194
BLI_INLINE int BLI_rcti_cent_y(const struct rcti *rct)
Definition BLI_rect.h:181
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.cc:198
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition BLI_rect.h:206
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:177
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.cc:41
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:599
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:688
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
ID_Type
@ ID_MC
@ ID_IM
@ ID_VF
@ IMA_SRC_FILE
@ IMA_SRC_MOVIE
@ IMA_SRC_GENERATED
@ IMA_SRC_VIEWER
@ IMA_SRC_TILED
@ IMA_SRC_SEQUENCE
@ RGN_TYPE_TEMPORARY
#define UI_SCALE_FAC
@ USER_TOOLTIPS_PYTHON
void ED_region_floating_init(ARegion *region)
Definition area.cc:2305
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:639
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_RGBA8
@ GPU_FETCH_FLOAT
uint GPU_vertformat_attr_add(GPUVertFormat *, blender::StringRef name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_F32
ImBuf * IMB_dupImBuf(const ImBuf *ibuf1)
void IMB_byte_from_float(ImBuf *ibuf)
void IMB_freeImBuf(ImBuf *ibuf)
ImBuf * IMB_allocImBuf(unsigned int x, unsigned int y, unsigned char planes, unsigned int flags)
bool IMB_scale(ImBuf *ibuf, unsigned int newx, unsigned int newy, IMBScaleFilter filter, bool threaded=true)
Definition scaling.cc:777
void IMB_rectfill_area(ImBuf *ibuf, const float col[4], int x1, int y1, int x2, int y2, const ColorManagedDisplay *display)
Definition rectop.cc:1172
@ IB_byte_data
ImBuf * IMB_font_preview(const char *filepath, unsigned int width, const float color[4])
Read Guarded memory(de)allocation.
@ IMB_TC_RECORD_RUN
Definition MOV_enums.hh:54
@ PROP_FLOAT
Definition RNA_types.hh:152
@ PROP_UNIT_ROTATION
Definition RNA_types.hh:166
@ PROP_PATH_SUPPORTS_BLEND_RELATIVE
Definition RNA_types.hh:430
@ PROP_ENUM_FLAG
Definition RNA_types.hh:378
@ PROP_PATH_SUPPORTS_TEMPLATES
Definition RNA_types.hh:443
PropertySubType
Definition RNA_types.hh:220
@ PROP_PASSWORD
Definition RNA_types.hh:231
@ PROP_NONE
Definition RNA_types.hh:221
@ PROP_DIRPATH
Definition RNA_types.hh:225
@ PROP_FILEPATH
Definition RNA_types.hh:224
#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)
bool UI_but_has_quick_tooltip(const uiBut *but)
@ UI_BUT_NO_TOOLTIP
int UI_but_unit_type_get(const uiBut *but)
#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)
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)
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)
@ UI_BLOCK_SHOW_SHORTCUT_ALWAYS
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)
@ UI_BUT_REDALERT
@ UI_BUT_DISABLED
@ UI_BUT_DRIVEN
void UI_GetThemeColor3fv(int colorid, float col[3])
@ TH_BACK
@ TH_REDALERT
void UI_GetThemeColor4fv(int colorid, float col[4])
@ KM_SHIFT
Definition WM_types.hh:275
wmOperatorCallContext
Definition WM_types.hh:236
@ WM_OP_INVOKE_REGION_WIN
Definition WM_types.hh:239
@ WM_OP_INVOKE_DEFAULT
Definition WM_types.hh:238
int pad[32 - sizeof(int)]
#define U
BMesh const char void * data
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 StringRef substr(int64_t start, int64_t size) const
constexpr bool startswith(StringRef prefix) const
constexpr int64_t size() const
constexpr const char & back() const
constexpr const char * c_str() const
bool is_empty() const
#define str(s)
uint pos
#define ID_IS_LINKED(_id)
#define ID_BLEND_PATH_FROM_GLOBAL(_id)
#define MAX_NAME
#define GS(a)
const ColorManagedDisplay * ui_block_cm_display_get(uiBlock *block)
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:165
void ui_but_v3_get(uiBut *but, float vec[3])
void ui_block_to_window_fl(const ARegion *region, const uiBlock *block, float *x, float *y)
Definition interface.cc:137
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)
ARegion * UI_tooltip_create_from_gizmo(bContext *C, wmGizmo *gz)
static std::unique_ptr< uiTooltipData > ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is_quick_tip)
#define UI_TIP_MAXWIDTH
static std::string ui_tooltip_text_python_from_op(bContext *C, wmOperatorType *ot, PointerRNA *opptr)
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)
ARegion * UI_tooltip_create_from_button(bContext *C, ARegion *butregion, uiBut *but, bool is_quick_tip)
#define UI_TIP_SPACER
static bool ui_tooltip_period_needed(blender::StringRef tip)
static std::string ui_tooltip_color_string(const blender::float4 &color, const blender::StringRefNull title, const bool show_alpha, const bool show_hex=false)
ARegion * UI_tooltip_create_from_button_or_extra_icon(bContext *C, ARegion *butregion, uiBut *but, uiButExtraOpIcon *extra_icon, bool is_quick_tip)
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)
#define UI_TIP_PADDING_Y
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_quick_tip)
#define UI_TIP_PADDING_X
static void color_blend_f3_f3(float dest[3], const float source[3], const float fac)
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)
format
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static ulong state[N]
static void error(const char *str)
ImBuf * MOV_decode_preview_frame(MovieReader *anim)
int MOV_get_duration_frames(MovieReader *anim, IMB_Timecode_Type tc)
int MOV_get_image_width(const MovieReader *anim)
int MOV_get_image_height(const MovieReader *anim)
StringRefNull essentials_directory_path()
VecBase< float, 4 > float4
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)
std::string RNA_property_string_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:1283
std::optional< std::string > RNA_path_full_struct_py(const PointerRNA *ptr)
Definition rna_path.cc:1267
void(* free)(ARegion *)
void(* draw)(const bContext *C, ARegion *region)
void * regiondata
ARegionRuntimeHandle * runtime
Definition DNA_ID.h:404
struct Library * lib
Definition DNA_ID.h:410
int us
Definition DNA_ID.h:425
char name[66]
Definition DNA_ID.h:415
ListBase anims
ColorManagedColorspaceSettings colorspace_settings
char filepath[1024]
short source
char filepath[1024]
Definition DNA_ID.h:507
ID id
Definition DNA_ID.h:505
void * first
struct MovieReader * anim
char filepath[1024]
ID * owner_id
Definition RNA_types.hh:51
void * data
Definition RNA_types.hh:53
int lines
Definition BLF_api.hh:481
int width
Definition BLF_api.hh:485
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
std::optional< uiTooltipImage > image
struct uiTooltipField::@316071106165061373226253007275065246040044235157 geom
uiTooltipImageBackground background
unsigned char inner[4]
unsigned char text[4]
int xy[2]
Definition WM_types.hh:758
wmOperatorType * type
PropertyRNA * prop
wmGizmoFnScreenBoundsGet screen_bounds_get
const wmGizmoType * type
blender::Vector< wmGizmoProperty, 0 > target_properties
wmOperatorType * optype
Definition WM_types.hh:1162
wmOperatorCallContext opcontext
Definition WM_types.hh:1164
const char * idname
Definition WM_types.hh:1032
struct wmEvent * eventstate
i
Definition text_draw.cc:230
static const char hex[17]
Definition thumbs.cc:160
wmOperatorType * ot
Definition wm_files.cc:4225
wmGizmoOpElem * WM_gizmo_operator_get(wmGizmo *gz, int part_index)
Definition wm_gizmo.cc:195
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)