Blender V5.0
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_utf8.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
62#include "IMB_imbuf.hh"
63#include "IMB_imbuf_types.hh"
64#include "IMB_thumbs.hh"
65
66#include "MOV_read.hh"
67
68#include "WM_api.hh"
69#include "WM_types.hh"
70
71#include "RNA_access.hh"
72#include "RNA_path.hh"
73#include "RNA_prototypes.hh"
74
75#include "BLF_api.hh"
76#include "BLT_translation.hh"
77
78#ifdef WITH_PYTHON
79# include "BPY_extern_run.hh"
80#endif
81
82#include "ED_screen.hh"
83
84#include "interface_intern.hh"
86
87/* Portions of line height. */
88#define UI_TIP_SPACER 0.3f
89#define UI_TIP_PADDING_X 1.95f
90#define UI_TIP_PADDING_Y 1.28f
91
92#define UI_TIP_MAXWIDTH 600
93
98
100 std::string text;
101 std::string text_suffix;
102 struct {
109 std::optional<uiTooltipImage> image;
110};
111
119
120BLI_STATIC_ASSERT(int(UI_TIP_LC_MAX) == int(UI_TIP_LC_ALERT) + 1, "invalid lc-max");
121
123 std::string text,
124 std::string suffix,
125 const uiTooltipStyle style,
126 const uiTooltipColorID color_id,
127 const bool is_pad)
128{
129 if (is_pad) {
130 /* Add a spacer field before this one. */
132 }
133
134 uiTooltipField field{};
135 field.format.style = style;
136 field.format.color_id = color_id;
137 field.text = std::move(text);
138 field.text_suffix = std::move(suffix);
139 data.fields.append(std::move(field));
140}
141
143{
144 uiTooltipField field{};
146 field.image = image_data;
147 field.image->ibuf = IMB_dupImBuf(image_data.ibuf);
148 data.fields.append(std::move(field));
149}
150
151/* -------------------------------------------------------------------- */
154
155static void color_blend_f3_f3(float dest[3], const float source[3], const float fac)
156{
157 if (fac != 0.0f) {
158 dest[0] = (1.0f - fac) * dest[0] + (fac * source[0]);
159 dest[1] = (1.0f - fac) * dest[1] + (fac * source[1]);
160 dest[2] = (1.0f - fac) * dest[2] + (fac * source[2]);
161 }
162}
163
164static void ui_tooltip_region_draw_cb(const bContext * /*C*/, ARegion *region)
165{
166 uiTooltipData *data = static_cast<uiTooltipData *>(region->regiondata);
167 const float pad_x = data->lineh * UI_TIP_PADDING_X;
168 const float pad_y = data->lineh * UI_TIP_PADDING_Y;
169 const uiWidgetColors *theme = ui_tooltip_get_theme();
170 rcti bbox = data->bbox;
171 float tip_colors[UI_TIP_LC_MAX][3];
172 uchar drawcol[4] = {0, 0, 0, 255}; /* to store color in while drawing (alpha is always 255) */
173
174 /* The color from the theme. */
175 float *main_color = tip_colors[UI_TIP_LC_MAIN];
176 float *value_color = tip_colors[UI_TIP_LC_VALUE];
177 float *active_color = tip_colors[UI_TIP_LC_ACTIVE];
178 float *normal_color = tip_colors[UI_TIP_LC_NORMAL];
179 float *python_color = tip_colors[UI_TIP_LC_PYTHON];
180 float *alert_color = tip_colors[UI_TIP_LC_ALERT];
181
182 float background_color[3];
183
185
186 /* Draw background. */
187 ui_draw_tooltip_background(UI_style_get(), nullptr, &bbox);
188
189 /* set background_color */
190 rgb_uchar_to_float(background_color, theme->inner);
191
192 /* `normal_color` is just tooltip text color. */
193 rgb_uchar_to_float(main_color, theme->text);
194 copy_v3_v3(normal_color, main_color);
195
196 /* `value_color` mixes with some background for less strength. */
197 copy_v3_v3(value_color, main_color);
198 color_blend_f3_f3(value_color, background_color, 0.2f);
199
200 /* `python_color` mixes with more background to be even dimmer. */
201 copy_v3_v3(python_color, main_color);
202 color_blend_f3_f3(python_color, background_color, 0.5f);
203
204 /* `active_color` is a light blue, push a bit toward text color. */
205 active_color[0] = 0.4f;
206 active_color[1] = 0.55f;
207 active_color[2] = 0.75f;
208 color_blend_f3_f3(active_color, main_color, 0.3f);
209
210 /* `alert_color` is red, push a bit toward text color. */
211 UI_GetThemeColor3fv(TH_REDALERT, alert_color);
212 color_blend_f3_f3(alert_color, main_color, 0.3f);
213
214 /* Draw text. */
215
216 /* Wrap most text typographically with hard width limit. */
217 BLF_wordwrap(data->fstyle.uifont_id,
218 data->wrap_width,
220
221 /* Wrap paths with path-specific wrapping with hard width limit. */
223 data->wrap_width,
225
226 bbox.xmin += 0.5f * pad_x; /* add padding to the text */
227 bbox.ymax -= 0.5f * pad_y;
228 bbox.ymax -= BLF_descender(data->fstyle.uifont_id);
229
230 for (int i = 0; i < data->fields.size(); i++) {
231 const uiTooltipField *field = &data->fields[i];
232
233 bbox.ymin = bbox.ymax - (data->lineh * field->geom.lines);
234 if (field->format.style == UI_TIP_STYLE_HEADER) {
235 uiFontStyleDraw_Params fs_params{};
236 fs_params.align = UI_STYLE_TEXT_LEFT;
237 fs_params.word_wrap = true;
238
239 /* Draw header and active data (is done here to be able to change color). */
240 rgb_float_to_uchar(drawcol, tip_colors[UI_TIP_LC_MAIN]);
241 UI_fontstyle_set(&data->fstyle);
243 &data->fstyle, &bbox, field->text.c_str(), field->text.size(), drawcol, &fs_params);
244
245 /* Offset to the end of the last line. */
246 if (!field->text_suffix.empty()) {
247 const float xofs = field->geom.x_pos;
248 const float yofs = data->lineh * (field->geom.lines - 1);
249 bbox.xmin += xofs;
250 bbox.ymax -= yofs;
251
252 rgb_float_to_uchar(drawcol, tip_colors[UI_TIP_LC_ACTIVE]);
253 UI_fontstyle_draw(&data->fstyle,
254 &bbox,
255 field->text_suffix.c_str(),
256 field->text_suffix.size(),
257 drawcol,
258 &fs_params);
259
260 /* Undo offset. */
261 bbox.xmin -= xofs;
262 bbox.ymax += yofs;
263 }
264 }
265 else if (field->format.style == UI_TIP_STYLE_MONO) {
266 uiFontStyleDraw_Params fs_params{};
267 fs_params.align = UI_STYLE_TEXT_LEFT;
268 fs_params.word_wrap = true;
269 uiFontStyle fstyle_mono = data->fstyle;
270 fstyle_mono.uifont_id = blf_mono_font;
271
272 UI_fontstyle_set(&fstyle_mono);
273 /* XXX: needed because we don't have mono in 'U.uifonts'. */
274 BLF_size(fstyle_mono.uifont_id, fstyle_mono.points * UI_SCALE_FAC);
275 rgb_float_to_uchar(drawcol, tip_colors[int(field->format.color_id)]);
277 &fstyle_mono, &bbox, field->text.c_str(), field->text.size(), drawcol, &fs_params);
278 }
279 else if (field->format.style == UI_TIP_STYLE_IMAGE && field->image.has_value()) {
280
281 bbox.ymax -= field->image->height;
282
283 if (field->image->background == uiTooltipImageBackground::Checkerboard_Themed) {
284 imm_draw_box_checker_2d(float(bbox.xmin),
285 float(bbox.ymax),
286 float(bbox.xmin + field->image->width),
287 float(bbox.ymax + field->image->height));
288 }
289 else if (field->image->background == uiTooltipImageBackground::Checkerboard_Fixed) {
290 const float checker_dark = UI_ALPHA_CHECKER_DARK / 255.0f;
291 const float checker_light = UI_ALPHA_CHECKER_LIGHT / 255.0f;
292 const float color1[4] = {checker_dark, checker_dark, checker_dark, 1.0f};
293 const float color2[4] = {checker_light, checker_light, checker_light, 1.0f};
294 imm_draw_box_checker_2d_ex(float(bbox.xmin + U.pixelsize),
295 float(bbox.ymax + U.pixelsize),
296 float(bbox.xmin + field->image->width),
297 float(bbox.ymax + field->image->height),
298 color1,
299 color2,
300 8);
301 }
302
303 GPU_blend((field->image->premultiplied) ? GPU_BLEND_ALPHA_PREMULT : GPU_BLEND_ALPHA);
304
307 bbox.xmin,
308 bbox.ymax,
309 field->image->ibuf->x,
310 field->image->ibuf->y,
311 blender::gpu::TextureFormat::UNORM_8_8_8_8,
312 true,
313 field->image->ibuf->byte_buffer.data,
314 1.0f,
315 1.0f,
316 float(field->image->width) / float(field->image->ibuf->x),
317 float(field->image->height) / float(field->image->ibuf->y),
318 (field->image->text_color) ? main_color : nullptr);
319
320 if (field->image->border) {
324 format, "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
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_UTF8(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_UTF8(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 std::optional<std::string> shortcut_toolbar = WM_key_event_operator_string(
633 C, "WM_OT_toolbar", blender::wm::OpCallContext::InvokeRegionWin, nullptr, true);
634 if (shortcut_toolbar) {
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_UTF8(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_UTF8(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_quick_tip, 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 int max_title_len,
809 const bool show_alpha,
810 const bool show_hex = false)
811{
812 const int align = max_title_len - title.size();
813
814 if (show_hex) {
815 uchar hex[4];
816 rgba_float_to_uchar(hex, color);
817 if (show_alpha) {
818 return fmt::format("{}:{: <{}} #{:02X}{:02X}{:02X}{:02X}",
819 title,
820 "",
821 align,
822 int(hex[0]),
823 int(hex[1]),
824 int(hex[2]),
825 int(hex[3]));
826 }
827 return fmt::format(
828 "{}:{: <{}} #{:02X}{:02X}{:02X}", title, "", align, int(hex[0]), int(hex[1]), int(hex[2]));
829 }
830
831 if (show_alpha) {
832 return fmt::format("{}:{: <{}} {:.3f}", title, "", align, color[3]);
833 }
834
835 return fmt::format(
836 "{}:{: <{}} {:.3f} {:.3f} {:.3f}", title, "", align, color[0], color[1], color[2]);
837};
838
840 const blender::float4 &original_color,
841 const bool has_alpha,
842 const bool is_gamma,
843 const ColorManagedDisplay *display,
844 const uiTooltipColorID color_id)
845{
846 blender::float4 scene_linear_color = original_color;
847 blender::float4 display_color = original_color;
848 blender::float4 srgb_color = original_color;
849
850 if (is_gamma) {
851 IMB_colormanagement_srgb_to_scene_linear_v3(scene_linear_color, scene_linear_color);
852 }
853 else {
855 display_color, display, DISPLAY_SPACE_COLOR_INSPECTION);
856 IMB_colormanagement_scene_linear_to_srgb_v3(srgb_color, srgb_color);
857 }
858
859 float hsv[4];
860 rgb_to_hsv_v(srgb_color, hsv);
861 hsv[3] = srgb_color[3];
862
863 const blender::StringRefNull hex_title = TIP_("Hex");
864 const blender::StringRefNull rgb_title = (is_gamma) ? TIP_("sRGB") : TIP_("Display RGB");
865 const blender::StringRefNull hsv_title = TIP_("HSV");
866 const blender::StringRefNull alpha_title = TIP_("Alpha");
867 const int max_title_len = std::max(
868 {hex_title.size(), rgb_title.size(), hsv_title.size(), alpha_title.size()});
869
870 const std::string hex_st = ui_tooltip_color_string(
871 srgb_color, hex_title, max_title_len, has_alpha, true);
872 const std::string rgba_st = ui_tooltip_color_string(
873 display_color, rgb_title, max_title_len, false);
874 const std::string hsv_st = ui_tooltip_color_string(hsv, hsv_title, max_title_len, false);
875 const std::string alpha_st = ui_tooltip_color_string(
876 scene_linear_color, alpha_title, max_title_len, true);
877
878 const uiFontStyle *fs = &UI_style_get()->tooltip;
880 float w = BLF_width(blf_mono_font, hsv_st.c_str(), hsv_st.size());
881
882 /* TODO: This clips wide gamut. Should make a float buffer and draw for display. */
883 uiTooltipImage image_data;
884 image_data.width = int(w);
885 image_data.height = int(w / (has_alpha ? 4.0f : 3.0f));
886 image_data.ibuf = IMB_allocImBuf(image_data.width, image_data.height, 32, IB_byte_data);
887 image_data.border = true;
888 image_data.premultiplied = false;
889
890 if (scene_linear_color[3] == 1.0f) {
891 /* No transparency so draw the entire area solid without checkerboard. */
894 image_data.ibuf, scene_linear_color, 1, 1, image_data.width, image_data.height);
895 }
896 else {
898 /* Draw one half with transparency. */
899 IMB_rectfill_area(image_data.ibuf,
900 scene_linear_color,
901 image_data.width / 2,
902 1,
903 image_data.width,
904 image_data.height);
905 /* Draw the other half with a solid color. */
906 scene_linear_color[3] = 1.0f;
908 image_data.ibuf, scene_linear_color, 1, 1, image_data.width / 2, image_data.height);
909 }
910
911 UI_tooltip_text_field_add(data, {}, {}, UI_TIP_STYLE_SPACER, color_id, false);
912 UI_tooltip_text_field_add(data, {}, {}, UI_TIP_STYLE_SPACER, color_id, false);
913 UI_tooltip_image_field_add(data, image_data);
914 UI_tooltip_text_field_add(data, {}, {}, UI_TIP_STYLE_SPACER, color_id, false);
915 UI_tooltip_text_field_add(data, rgba_st, {}, UI_TIP_STYLE_MONO, color_id, false);
916 UI_tooltip_text_field_add(data, hsv_st, {}, UI_TIP_STYLE_MONO, color_id, false);
917 if (has_alpha) {
918 UI_tooltip_text_field_add(data, alpha_st, {}, UI_TIP_STYLE_MONO, color_id, false);
919 }
920 UI_tooltip_text_field_add(data, {}, {}, UI_TIP_STYLE_SPACER, color_id, false);
921 UI_tooltip_text_field_add(data, hex_st, {}, UI_TIP_STYLE_MONO, color_id, false);
922
923 /* Tooltip now owns a copy of the ImBuf, so we can delete ours. */
924 IMB_freeImBuf(image_data.ibuf);
925}
926
928 bContext &C,
929 uiBut &but,
930 uiButExtraOpIcon *extra_icon)
931{
932 wmOperatorType *optype = extra_icon ? UI_but_extra_operator_icon_optype_get(extra_icon) :
933 but.optype;
934 PropertyRNA *rnaprop = extra_icon ? nullptr : but.rnaprop;
935 std::string rna_struct = UI_but_string_get_rna_struct_identifier(but);
936 std::string rna_prop = UI_but_string_get_rna_property_identifier(but);
937
938 if (optype && !rnaprop) {
939 PointerRNA *opptr = extra_icon ? UI_but_extra_operator_icon_opptr_get(extra_icon) :
940 /* Allocated when needed, the button owns it. */
942
943 /* So the context is passed to field functions (some Python field functions use it). */
945
946 std::string str = ui_tooltip_text_python_from_op(&C, optype, opptr);
947
948 /* Operator info. */
950 fmt::format(fmt::runtime(TIP_("Python: {}")), str),
951 {},
954 true);
955 }
956
957 if (!optype && !rna_struct.empty()) {
958 {
960 data,
961 rna_prop.empty() ?
962 fmt::format(fmt::runtime(TIP_("Python: {}")), rna_struct) :
963 fmt::format(fmt::runtime(TIP_("Python: {}.{}")), rna_struct, rna_prop),
964 {},
967 (data.fields.size() > 0));
968 }
969
970 if (but.rnapoin.owner_id) {
971 std::optional<std::string> str = rnaprop ? RNA_path_full_property_py_ex(
972 &but.rnapoin, rnaprop, but.rnaindex, true) :
975 }
976 }
977}
978
979static std::unique_ptr<uiTooltipData> ui_tooltip_data_from_button_or_extra_icon(
980 bContext *C, uiBut *but, uiButExtraOpIcon *extra_icon, const bool is_quick_tip)
981{
982 char buf[512];
983
984 wmOperatorType *optype = extra_icon ? UI_but_extra_operator_icon_optype_get(extra_icon) :
985 but->optype;
986 PropertyRNA *rnaprop = extra_icon ? nullptr : but->rnaprop;
987
988 std::unique_ptr<uiTooltipData> data = std::make_unique<uiTooltipData>();
989
990 /* Menus already show shortcuts, don't show them in the tool-tips. */
991 const bool is_menu = ui_block_is_menu(but->block) && !ui_block_is_pie_menu(but->block);
992
993 std::string but_label;
994 std::string but_tip;
995 std::string but_tip_label;
996 std::string op_keymap;
997 std::string prop_keymap;
998 std::string rna_struct;
999 std::string rna_prop;
1000 std::string enum_label;
1001 std::string enum_tip;
1002
1003 if (extra_icon) {
1004 if (is_quick_tip) {
1005 but_label = UI_but_extra_icon_string_get_label(*extra_icon);
1006 }
1007 else {
1008 but_label = UI_but_extra_icon_string_get_label(*extra_icon);
1009 but_tip = UI_but_extra_icon_string_get_tooltip(*C, *extra_icon);
1010 if (!is_menu) {
1011 op_keymap = UI_but_extra_icon_string_get_operator_keymap(*C, *extra_icon);
1012 }
1013 }
1014 }
1015 else {
1016 const std::optional<EnumPropertyItem> enum_item = UI_but_rna_enum_item_get(*C, *but);
1017 if (is_quick_tip) {
1018 but_tip_label = UI_but_string_get_tooltip_label(*but);
1019 but_label = UI_but_string_get_label(*but);
1020 enum_label = enum_item ? enum_item->name : "";
1021 }
1022 else {
1023 but_label = UI_but_string_get_label(*but);
1024 but_tip_label = UI_but_string_get_tooltip_label(*but);
1025 but_tip = UI_but_string_get_tooltip(*C, *but);
1026 enum_label = enum_item ? enum_item->name : "";
1027 const char *description_c = enum_item ? enum_item->description : nullptr;
1028 enum_tip = description_c ? description_c : "";
1029 if (!is_menu) {
1030 op_keymap = UI_but_string_get_operator_keymap(*C, *but);
1031 prop_keymap = UI_but_string_get_property_keymap(*C, *but);
1032 }
1033 rna_struct = UI_but_string_get_rna_struct_identifier(*but);
1035 }
1036 }
1037
1038 /* Label: If there is a custom tooltip label, use that to override the label to display.
1039 * Otherwise fallback to the regular label. */
1040 if (!but_tip_label.empty()) {
1042 if (!is_quick_tip) {
1044 }
1045 }
1046 /* Regular (non-custom) label. Only show when the button doesn't already show the label. Check
1047 * prefix instead of comparing because the button may include the shortcut. Buttons with dynamic
1048 * tool-tips also don't get their default label here since they can already provide more accurate
1049 * and specific tool-tip content. */
1050 else if (!but_label.empty() && !blender::StringRef(but->drawstr).startswith(but_label) &&
1051 !but->tip_func)
1052 {
1053 if (!enum_label.empty()) {
1055 fmt::format("{}: ", but_label),
1056 enum_label,
1059 }
1060 else {
1062 }
1064 }
1065
1066 /* Tip */
1067 if (!but_tip.empty()) {
1068 if (!enum_label.empty() && enum_label == but_label) {
1070 *data, fmt::format("{}: ", but_tip), enum_label, UI_TIP_STYLE_HEADER, UI_TIP_LC_NORMAL);
1072 }
1073 else {
1074 const bool add_period = ui_tooltip_period_needed(but_tip);
1076 fmt::format("{}{}", but_tip, add_period ? "." : ""),
1077 {},
1080 if (but_label.empty()) {
1082 }
1083 }
1084
1085 /* special case enum rna buttons */
1086 if ((but->type == ButType::Row) && rnaprop && RNA_property_flag(rnaprop) & PROP_ENUM_FLAG) {
1088 TIP_("(Shift-Click/Drag to select multiple)"),
1089 {},
1092 }
1093 }
1094 /* When there is only an enum label (no button label or tip), draw that as header. */
1095 else if (!enum_label.empty() && but_label.empty()) {
1097 *data, std::move(enum_label), {}, UI_TIP_STYLE_HEADER, UI_TIP_LC_NORMAL);
1098 }
1099
1100 /* Don't include further details if this is just a quick label tooltip. */
1101 if (is_quick_tip) {
1102 return data->fields.is_empty() ? nullptr : std::move(data);
1103 }
1104
1105 /* Enum field label & tip. */
1106 if (!enum_tip.empty()) {
1108 *data, std::move(enum_tip), {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_VALUE);
1109 }
1110
1111 /* Operator shortcut. */
1112 if (!op_keymap.empty()) {
1114 fmt::format(fmt::runtime(TIP_("Shortcut: {}")), op_keymap),
1115 {},
1118 !data->fields.is_empty());
1119 }
1120
1121 /* Property context-toggle shortcut. */
1122 if (!prop_keymap.empty()) {
1124 fmt::format(fmt::runtime(TIP_("Shortcut: {}")), prop_keymap),
1125 {},
1128 true);
1129 }
1130
1132 /* Better not show the value of a password. */
1133 if ((rnaprop && (RNA_property_subtype(rnaprop) == PROP_PASSWORD)) == 0) {
1134 /* Full string. */
1135 ui_but_string_get(but, buf, sizeof(buf));
1136 if (buf[0]) {
1138 fmt::format(fmt::runtime(TIP_("Value: {}")), buf),
1139 {},
1142 true);
1143 }
1144 }
1145 }
1146
1147 if (rnaprop) {
1148 const int unit_type = UI_but_unit_type_get(but);
1149
1150 if (unit_type == PROP_UNIT_ROTATION) {
1151 if (RNA_property_type(rnaprop) == PROP_FLOAT) {
1152 float value = RNA_property_array_check(rnaprop) ?
1153 RNA_property_float_get_index(&but->rnapoin, rnaprop, but->rnaindex) :
1154 RNA_property_float_get(&but->rnapoin, rnaprop);
1156 fmt::format(fmt::runtime(TIP_("Radians: {}")), value),
1157 {},
1160 }
1161 }
1162
1163 if (but->flag & UI_BUT_DRIVEN) {
1164 if (ui_but_anim_expression_get(but, buf, sizeof(buf))) {
1166 fmt::format(fmt::runtime(TIP_("Expression: {}")), buf),
1167 {},
1170 }
1171 }
1172
1173 if (but->rnapoin.owner_id) {
1174 const ID *id = but->rnapoin.owner_id;
1175 if (ID_IS_LINKED(id)) {
1177 const bool is_builtin = BLI_path_contains(assets_path.c_str(), id->lib->filepath);
1178 const blender::StringRef title = is_builtin ? TIP_("Built-in Asset") : TIP_("Library");
1179 const blender::StringRef lib_path = id->lib->filepath;
1180 const blender::StringRef path = is_builtin ? lib_path.substr(assets_path.size()) :
1181 id->lib->filepath;
1183 *data, fmt::format("{}: {}", title, path), {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_NORMAL);
1184 }
1185 }
1186 }
1187
1188 /* Warn on path validity errors. */
1189 if (ELEM(but->type, ButType::Text) &&
1190 /* Check red-alert, if the flag is not set, then this was suppressed. */
1191 (but->flag & UI_BUT_REDALERT))
1192 {
1193 if (rnaprop) {
1194 PropertySubType subtype = RNA_property_subtype(rnaprop);
1195
1196 /* If relative paths are used when unsupported (will already display red-alert). */
1197 if (ELEM(subtype, PROP_FILEPATH, PROP_DIRPATH)) {
1199 if (BLI_path_is_rel(but->drawstr.c_str())) {
1201 "Warning: the blend-file relative path prefix \"//\" "
1202 "is not supported for this property.",
1203 {},
1206 }
1207 }
1208 }
1209
1210 /* We include PROP_NONE here because some plain string properties are used
1211 * as parts of paths. For example, the sub-paths in the compositor's File
1212 * Output node. */
1214 /* Template parse errors, for paths that support it. */
1215 if ((RNA_property_flag(rnaprop) & PROP_PATH_SUPPORTS_TEMPLATES) != 0) {
1216 const std::string path = RNA_property_string_get(&but->rnapoin, rnaprop);
1218 const std::optional<blender::bke::path_templates::VariableMap> variables =
1220 BLI_assert(variables.has_value());
1221
1223 BKE_path_validate_template(path, *variables);
1224
1225 if (!errors.is_empty()) {
1226 std::string error_message("Path template error(s):");
1227 for (const blender::bke::path_templates::Error &error : errors) {
1228 error_message += "\n - " + BKE_path_template_error_to_string(error, path);
1229 }
1231 *data, error_message, {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_ALERT);
1232 }
1233 }
1234 }
1235 }
1236 }
1237 }
1238
1239 /* Button is disabled, we may be able to tell user why. */
1240 if ((but->flag & UI_BUT_DISABLED) || extra_icon) {
1241 const char *disabled_msg_orig = nullptr;
1242 const char *disabled_msg = nullptr;
1243 bool disabled_msg_free = false;
1244
1245 /* If operator poll check failed, it can give pretty precise info why. */
1246 if (optype) {
1247 const blender::wm::OpCallContext opcontext = extra_icon ?
1248 extra_icon->optype_params->opcontext :
1249 but->opcontext;
1250 wmOperatorCallParams call_params{};
1251 call_params.optype = optype;
1252 call_params.opcontext = opcontext;
1254 ui_but_context_poll_operator_ex(C, but, &call_params);
1255 disabled_msg_orig = CTX_wm_operator_poll_msg_get(C, &disabled_msg_free);
1256 disabled_msg = TIP_(disabled_msg_orig);
1257 }
1258 /* Alternatively, buttons can store some reasoning too. */
1259 else if (!extra_icon && but->disabled_info) {
1260 disabled_msg = TIP_(but->disabled_info);
1261 }
1262
1263 if (disabled_msg && disabled_msg[0]) {
1265 fmt::format(fmt::runtime(TIP_("Disabled: {}")), disabled_msg),
1266 {},
1269 }
1270 if (disabled_msg_free) {
1271 MEM_freeN(disabled_msg_orig);
1272 }
1273 }
1274
1275 if (U.flag & USER_TOOLTIPS_PYTHON) {
1276 UI_tooltip_uibut_python_add(*data, *C, *but, extra_icon);
1277 }
1278
1279 if (but->type == ButType::Color) {
1280 const ColorManagedDisplay *display = UI_but_cm_display_get(*but);
1281
1282 float color[4];
1283 ui_but_v3_get(but, color);
1284 color[3] = 1.0f;
1285 bool has_alpha = false;
1286
1287 if (but->rnaprop) {
1288 BLI_assert(but->rnaindex == -1);
1289 has_alpha = RNA_property_array_length(&but->rnapoin, but->rnaprop) >= 4;
1290 if (has_alpha) {
1291 color[3] = RNA_property_float_get_index(&but->rnapoin, but->rnaprop, 3);
1292 }
1293 }
1294
1296 *data, color, has_alpha, ui_but_is_color_gamma(but), display, UI_TIP_LC_NORMAL);
1297 }
1298
1299 /* If the last field is a spacer, remove it. */
1300 while (!data->fields.is_empty() && data->fields.last().format.style == UI_TIP_STYLE_SPACER) {
1301 data->fields.pop_last();
1302 }
1303
1304 return data->fields.is_empty() ? nullptr : std::move(data);
1305}
1306
1307static std::unique_ptr<uiTooltipData> ui_tooltip_data_from_gizmo(bContext *C, wmGizmo *gz)
1308{
1309 std::unique_ptr<uiTooltipData> data = std::make_unique<uiTooltipData>();
1310
1311 /* TODO(@ideasman42): a way for gizmos to have their own descriptions (low priority). */
1312
1313 /* Operator Actions */
1314 {
1315 const bool use_drag = gz->drag_part != -1 && gz->highlight_part != gz->drag_part;
1316 struct GizmoOpActions {
1317 int part;
1318 const char *prefix;
1319 };
1320 GizmoOpActions gzop_actions[] = {
1321 {
1322 gz->highlight_part,
1323 use_drag ? CTX_TIP_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Click") : nullptr,
1324 },
1325 {
1326 use_drag ? gz->drag_part : -1,
1327 use_drag ? CTX_TIP_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Drag") : nullptr,
1328 },
1329 };
1330
1331 for (int i = 0; i < ARRAY_SIZE(gzop_actions); i++) {
1332 wmGizmoOpElem *gzop = (gzop_actions[i].part != -1) ?
1333 WM_gizmo_operator_get(gz, gzop_actions[i].part) :
1334 nullptr;
1335 if (gzop != nullptr) {
1336 /* Description */
1337 std::string info = WM_operatortype_description_or_name(C, gzop->type, &gzop->ptr);
1338
1339 if (!info.empty()) {
1341 *data,
1342 gzop_actions[i].prefix ? fmt::format("{}: {}", gzop_actions[i].prefix, info) : info,
1343 {},
1346 false);
1347 }
1348
1349 /* Shortcut */
1350 {
1351 IDProperty *prop = static_cast<IDProperty *>(gzop->ptr.data);
1352 std::optional<std::string> shortcut_str = WM_key_event_operator_string(
1354 if (shortcut_str) {
1356 *data,
1357 fmt::format(fmt::runtime(TIP_("Shortcut: {}")), *shortcut_str),
1358 {},
1361 true);
1362 }
1363 }
1364 }
1365 }
1366 }
1367
1368 /* Property Actions */
1369 for (const wmGizmoProperty &gz_prop : gz->target_properties) {
1370 if (gz_prop.prop != nullptr) {
1371 const char *info = RNA_property_ui_description(gz_prop.prop);
1372 if (info && info[0]) {
1374 }
1375 }
1376 }
1377
1378 return data->fields.is_empty() ? nullptr : std::move(data);
1379}
1380
1381static std::unique_ptr<uiTooltipData> ui_tooltip_data_from_custom_func(bContext *C, uiBut *but)
1382{
1383 /* Create tooltip data. */
1384 std::unique_ptr<uiTooltipData> data = std::make_unique<uiTooltipData>();
1385
1386 /* Create fields from custom callback. */
1387 but->tip_custom_func(*C, *data, but, but->tip_arg);
1388
1389 return data->fields.is_empty() ? nullptr : std::move(data);
1390}
1391
1393 std::unique_ptr<uiTooltipData> data_uptr,
1394 const float init_position[2],
1395 const rcti *init_rect_overlap)
1396{
1397 wmWindow *win = CTX_wm_window(C);
1398 const blender::int2 win_size = WM_window_native_pixel_size(win);
1399 rcti rect_i;
1400 FontFlags font_flag = BLF_NONE;
1401
1402 /* Create area region. */
1404
1405 static ARegionType type;
1406 memset(&type, 0, sizeof(ARegionType));
1410 region->runtime->type = &type;
1411 /* Move ownership to region data. The region type free callback puts it back into a unique
1412 * pointer for save freeing. */
1413 region->regiondata = data_uptr.release();
1414
1415 uiTooltipData *data = static_cast<uiTooltipData *>(region->regiondata);
1416
1417 /* Set font, get bounding-box. */
1418 const uiStyle *style = UI_style_get();
1419 data->fstyle = style->tooltip; /* copy struct */
1420 BLF_size(data->fstyle.uifont_id, data->fstyle.points * UI_SCALE_FAC);
1421 int h = BLF_height_max(data->fstyle.uifont_id);
1422 const float pad_x = h * UI_TIP_PADDING_X;
1423 const float pad_y = h * UI_TIP_PADDING_Y;
1424
1425 UI_fontstyle_set(&data->fstyle);
1426
1427 data->wrap_width = min_ii(UI_TIP_MAXWIDTH * UI_SCALE_FAC, win_size[0] - pad_x);
1428
1429 font_flag |= BLF_WORD_WRAP;
1430 BLF_enable(data->fstyle.uifont_id, font_flag);
1431 BLF_enable(blf_mono_font, font_flag);
1432 BLF_wordwrap(data->fstyle.uifont_id,
1433 data->wrap_width,
1436 data->wrap_width,
1438
1439 int i, fonth, fontw;
1440 for (i = 0, fontw = 0, fonth = 0; i < data->fields.size(); i++) {
1441 uiTooltipField *field = &data->fields[i];
1442 ResultBLF info = {0};
1443 int w = 0;
1444 int x_pos = 0;
1445 int font_id;
1446
1447 if (field->format.style == UI_TIP_STYLE_MONO) {
1448 BLF_size(blf_mono_font, data->fstyle.points * UI_SCALE_FAC);
1449 font_id = blf_mono_font;
1450 }
1451 else {
1452 font_id = data->fstyle.uifont_id;
1453 }
1454
1455 if (!field->text.empty()) {
1456 w = BLF_width(font_id, field->text.c_str(), field->text.size(), &info);
1457 }
1458
1459 /* check for suffix (enum label) */
1460 if (!field->text_suffix.empty()) {
1461 x_pos = info.width;
1462 w = max_ii(w,
1463 x_pos + BLF_width(font_id, ": ", BLF_DRAW_STR_DUMMY_MAX) +
1464 BLF_width(font_id, field->text_suffix.c_str(), BLF_DRAW_STR_DUMMY_MAX));
1465 }
1466
1467 fonth += h * info.lines;
1468
1469 if (field->format.style == UI_TIP_STYLE_SPACER) {
1470 fonth += h * UI_TIP_SPACER;
1471 }
1472
1473 if (field->format.style == UI_TIP_STYLE_IMAGE && field->image) {
1474 fonth += field->image->height;
1475 w = max_ii(w, field->image->width);
1476 }
1477
1478 fontw = max_ii(fontw, w);
1479
1480 field->geom.lines = info.lines;
1481 field->geom.x_pos = x_pos;
1482 }
1483
1484 BLF_disable(data->fstyle.uifont_id, font_flag);
1485 BLF_disable(blf_mono_font, font_flag);
1486
1487 data->toth = fonth;
1488 data->lineh = h;
1489
1490 /* Compute position. */
1491 {
1492 rctf rect_fl;
1493 rect_fl.xmin = init_position[0] - (h * 0.2f) - (pad_x * 0.5f);
1494 rect_fl.xmax = rect_fl.xmin + fontw;
1495 rect_fl.ymax = init_position[1] - (h * 0.2f) - (pad_y * 0.5f);
1496 rect_fl.ymin = rect_fl.ymax - fonth;
1497 BLI_rcti_rctf_copy(&rect_i, &rect_fl);
1498 }
1499
1500 // #define USE_ALIGN_Y_CENTER
1501
1502 /* Clamp to window bounds. */
1503 {
1504 /* Ensure at least 5 pixels above screen bounds.
1505 * #UI_UNIT_Y is just a guess to be above the menu item. */
1506 if (init_rect_overlap != nullptr) {
1507 const int pad = max_ff(1.0f, U.pixelsize) * 5;
1508 rcti init_rect;
1509 init_rect.xmin = init_rect_overlap->xmin - pad;
1510 init_rect.xmax = init_rect_overlap->xmax + pad;
1511 init_rect.ymin = init_rect_overlap->ymin - pad;
1512 init_rect.ymax = init_rect_overlap->ymax + pad;
1513 rcti rect_clamp;
1514 rect_clamp.xmin = pad_x + pad;
1515 rect_clamp.xmax = win_size[0] - pad_x - pad;
1516 rect_clamp.ymin = pad_y + pad;
1517 rect_clamp.ymax = win_size[1] - pad_y - pad;
1518 /* try right. */
1519 const int size_x = BLI_rcti_size_x(&rect_i);
1520 const int size_y = BLI_rcti_size_y(&rect_i);
1521 const int cent_overlap_x = BLI_rcti_cent_x(&init_rect);
1522#ifdef USE_ALIGN_Y_CENTER
1523 const int cent_overlap_y = BLI_rcti_cent_y(&init_rect);
1524#endif
1525 struct {
1526 rcti xpos;
1527 rcti xneg;
1528 rcti ypos;
1529 rcti yneg;
1530 } rect;
1531
1532 { /* xpos */
1533 rcti r = rect_i;
1534 r.xmin = init_rect.xmax;
1535 r.xmax = r.xmin + size_x;
1536#ifdef USE_ALIGN_Y_CENTER
1537 r.ymin = cent_overlap_y - (size_y / 2);
1538 r.ymax = r.ymin + size_y;
1539#else
1540 r.ymin = init_rect.ymax - BLI_rcti_size_y(&rect_i);
1541 r.ymax = init_rect.ymax;
1542 r.ymin -= UI_POPUP_MARGIN;
1543 r.ymax -= UI_POPUP_MARGIN;
1544#endif
1545 rect.xpos = r;
1546 }
1547 { /* xneg */
1548 rcti r = rect_i;
1549 r.xmin = init_rect.xmin - size_x;
1550 r.xmax = r.xmin + size_x;
1551#ifdef USE_ALIGN_Y_CENTER
1552 r.ymin = cent_overlap_y - (size_y / 2);
1553 r.ymax = r.ymin + size_y;
1554#else
1555 r.ymin = init_rect.ymax - BLI_rcti_size_y(&rect_i);
1556 r.ymax = init_rect.ymax;
1557 r.ymin -= UI_POPUP_MARGIN;
1558 r.ymax -= UI_POPUP_MARGIN;
1559#endif
1560 rect.xneg = r;
1561 }
1562 { /* ypos */
1563 rcti r = rect_i;
1564 r.xmin = cent_overlap_x - (size_x / 2);
1565 r.xmax = r.xmin + size_x;
1566 r.ymin = init_rect.ymax;
1567 r.ymax = r.ymin + size_y;
1568 rect.ypos = r;
1569 }
1570 { /* yneg */
1571 rcti r = rect_i;
1572 r.xmin = cent_overlap_x - (size_x / 2);
1573 r.xmax = r.xmin + size_x;
1574 r.ymin = init_rect.ymin - size_y;
1575 r.ymax = r.ymin + size_y;
1576 rect.yneg = r;
1577 }
1578
1579 bool found = false;
1580 for (int j = 0; j < 4; j++) {
1581 const rcti *r = (&rect.xpos) + j;
1582 if (BLI_rcti_inside_rcti(&rect_clamp, r)) {
1583 rect_i = *r;
1584 found = true;
1585 break;
1586 }
1587 }
1588 if (!found) {
1589 /* Fallback, we could pick the best fallback, for now just use xpos. */
1590 int offset_dummy[2];
1591 rect_i = rect.xpos;
1592 BLI_rcti_clamp(&rect_i, &rect_clamp, offset_dummy);
1593 }
1594 }
1595 else {
1596 const int clamp_pad_x = int((5.0f * UI_SCALE_FAC) + (pad_x * 0.5f));
1597 const int clamp_pad_y = int((7.0f * UI_SCALE_FAC) + (pad_y * 0.5f));
1598 rcti rect_clamp;
1599 rect_clamp.xmin = clamp_pad_x;
1600 rect_clamp.xmax = win_size[0] - clamp_pad_x;
1601 rect_clamp.ymin = clamp_pad_y;
1602 rect_clamp.ymax = win_size[1] - clamp_pad_y;
1603 int offset_dummy[2];
1604 BLI_rcti_clamp(&rect_i, &rect_clamp, offset_dummy);
1605 }
1606 }
1607
1608#undef USE_ALIGN_Y_CENTER
1609
1610 /* add padding */
1611 BLI_rcti_pad(&rect_i, int(round(pad_x * 0.5f)), int(round(pad_y * 0.5f)));
1612
1613 /* widget rect, in region coords */
1614 {
1615 /* Compensate for margin offset, visually this corrects the position. */
1616 const int margin = UI_POPUP_MARGIN;
1617 if (init_rect_overlap != nullptr) {
1618 BLI_rcti_translate(&rect_i, margin, margin / 2);
1619 }
1620
1621 data->bbox.xmin = margin;
1622 data->bbox.xmax = BLI_rcti_size_x(&rect_i) + margin;
1623 data->bbox.ymin = margin;
1624 data->bbox.ymax = BLI_rcti_size_y(&rect_i) + margin;
1625
1626 /* region bigger for shadow */
1627 region->winrct.xmin = rect_i.xmin - margin;
1628 region->winrct.xmax = rect_i.xmax + margin;
1629 region->winrct.ymin = rect_i.ymin - margin;
1630 region->winrct.ymax = rect_i.ymax + margin;
1631 }
1632
1633 /* Adds sub-window. */
1635
1636 /* notify change and redraw */
1637 ED_region_tag_redraw(region);
1638
1639 return region;
1640}
1641
1643
1644/* -------------------------------------------------------------------- */
1647
1649 bContext *C, ARegion *butregion, uiBut *but, uiButExtraOpIcon *extra_icon, bool is_quick_tip)
1650{
1651 wmWindow *win = CTX_wm_window(C);
1652 float init_position[2];
1653
1654 if (but->drawflag & UI_BUT_NO_TOOLTIP) {
1655 return nullptr;
1656 }
1657 std::unique_ptr<uiTooltipData> data = nullptr;
1658
1659 if (!is_quick_tip && but->tip_custom_func) {
1661 }
1662
1663 if (data == nullptr) {
1664 data = ui_tooltip_data_from_tool(C, but, is_quick_tip);
1665 }
1666
1667 if (data == nullptr) {
1668 data = ui_tooltip_data_from_button_or_extra_icon(C, but, extra_icon, is_quick_tip);
1669 }
1670
1671 if (data == nullptr) {
1672 data = ui_tooltip_data_from_button_or_extra_icon(C, but, nullptr, is_quick_tip);
1673 }
1674
1675 if (data == nullptr) {
1676 return nullptr;
1677 }
1678
1679 const bool is_no_overlap = UI_but_has_quick_tooltip(but) || UI_but_is_tool(but);
1680 rcti init_rect;
1681 if (is_no_overlap) {
1682 rctf overlap_rect_fl;
1683 init_position[0] = BLI_rctf_cent_x(&but->rect);
1684 init_position[1] = BLI_rctf_cent_y(&but->rect);
1685 if (butregion) {
1686 ui_block_to_window_fl(butregion, but->block, &init_position[0], &init_position[1]);
1687 ui_block_to_window_rctf(butregion, but->block, &overlap_rect_fl, &but->rect);
1688 }
1689 else {
1690 overlap_rect_fl = but->rect;
1691 }
1692 BLI_rcti_rctf_copy_round(&init_rect, &overlap_rect_fl);
1693 }
1694 else if (but->type == ButType::Label && BLI_rctf_size_y(&but->rect) > UI_UNIT_Y) {
1695 init_position[0] = win->eventstate->xy[0];
1696 init_position[1] = win->eventstate->xy[1] - (UI_POPUP_MARGIN / 2);
1697 }
1698 else {
1699 init_position[0] = BLI_rctf_cent_x(&but->rect);
1700 init_position[1] = but->rect.ymin;
1701 if (butregion) {
1702 ui_block_to_window_fl(butregion, but->block, &init_position[0], &init_position[1]);
1703 init_position[0] = win->eventstate->xy[0];
1704 }
1705 init_position[1] -= (UI_POPUP_MARGIN / 2);
1706 }
1707
1709 C, std::move(data), init_position, is_no_overlap ? &init_rect : nullptr);
1710
1711 return region;
1712}
1713
1715 ARegion *butregion,
1716 uiBut *but,
1717 bool is_quick_tip)
1718{
1719 return UI_tooltip_create_from_button_or_extra_icon(C, butregion, but, nullptr, is_quick_tip);
1720}
1721
1723{
1724 wmWindow *win = CTX_wm_window(C);
1725 float init_position[2] = {float(win->eventstate->xy[0]), float(win->eventstate->xy[1])};
1726
1727 std::unique_ptr<uiTooltipData> data = ui_tooltip_data_from_gizmo(C, gz);
1728 if (data == nullptr) {
1729 return nullptr;
1730 }
1731
1732 /* TODO(@harley): Julian preferred that the gizmo callback return the 3D bounding box
1733 * which we then project to 2D here. Would make a nice improvement. */
1734 if (gz->type->screen_bounds_get) {
1735 rcti bounds;
1736 if (gz->type->screen_bounds_get(C, gz, &bounds)) {
1737 init_position[0] = bounds.xmin;
1738 init_position[1] = bounds.ymin;
1739 }
1740 }
1741
1742 return ui_tooltip_create_with_data(C, std::move(data), init_position, nullptr);
1743}
1744
1746{
1747 if (ima.filepath[0]) {
1748 char root[FILE_MAX];
1751 }
1752
1753 std::string image_type;
1754 switch (ima.source) {
1755 case IMA_SRC_FILE:
1756 image_type = TIP_("Single Image");
1757 break;
1758 case IMA_SRC_SEQUENCE:
1759 image_type = TIP_("Image Sequence");
1760 break;
1761 case IMA_SRC_MOVIE:
1762 image_type = TIP_("Movie");
1763 break;
1764 case IMA_SRC_GENERATED:
1765 image_type = TIP_("Generated");
1766 break;
1767 case IMA_SRC_VIEWER:
1768 image_type = TIP_("Viewer");
1769 break;
1770 case IMA_SRC_TILED:
1771 image_type = TIP_("UDIM Tiles");
1772 break;
1773 }
1775
1776 short w;
1777 short h;
1778 ImBuf *ibuf = BKE_image_preview(&ima, 200.0f * UI_SCALE_FAC, &w, &h);
1779
1780 if (ibuf) {
1782 data, fmt::format("{} \u00D7 {}", w, h), {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_NORMAL);
1783 }
1784
1785 if (BKE_image_has_anim(&ima)) {
1786 MovieReader *anim = static_cast<ImageAnim *>(ima.anims.first)->anim;
1787 if (anim) {
1788 int duration = MOV_get_duration_frames(anim, IMB_TC_RECORD_RUN);
1790 data, fmt::format("Frames: {}", duration), {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_NORMAL);
1791 }
1792 }
1793
1796
1798 fmt::format(fmt::runtime(TIP_("Users: {}")), ima.id.us),
1799 {},
1802
1803 if (ibuf) {
1804 uiTooltipImage image_data;
1805 image_data.width = ibuf->x;
1806 image_data.height = ibuf->y;
1807 image_data.ibuf = ibuf;
1808 image_data.border = true;
1810 image_data.premultiplied = true;
1813 UI_tooltip_image_field_add(data, image_data);
1814 IMB_freeImBuf(ibuf);
1815 }
1816}
1817
1819{
1820 if (clip.filepath[0]) {
1821 char root[FILE_MAX];
1824 }
1825
1826 std::string image_type;
1827 switch (clip.source) {
1828 case IMA_SRC_SEQUENCE:
1829 image_type = TIP_("Image Sequence");
1830 break;
1831 case IMA_SRC_MOVIE:
1832 image_type = TIP_("Movie");
1833 break;
1834 }
1836
1837 if (clip.anim) {
1838 MovieReader *anim = clip.anim;
1839
1841 data,
1842 fmt::format("{} \u00D7 {}", MOV_get_image_width(anim), MOV_get_image_height(anim)),
1843 {},
1846
1848 data,
1849 fmt::format("Frames: {}", MOV_get_duration_frames(anim, IMB_TC_RECORD_RUN)),
1850 {},
1853
1854 ImBuf *ibuf = MOV_decode_preview_frame(anim);
1855
1856 if (ibuf) {
1857 /* Resize. */
1858 float scale = (200.0f * UI_SCALE_FAC) / float(std::max(ibuf->x, ibuf->y));
1859 IMB_scale(ibuf, scale * ibuf->x, scale * ibuf->y, IMBScaleFilter::Box, false);
1860 IMB_byte_from_float(ibuf);
1861
1862 uiTooltipImage image_data;
1863 image_data.width = ibuf->x;
1864 image_data.height = ibuf->y;
1865 image_data.ibuf = ibuf;
1866 image_data.border = true;
1868 image_data.premultiplied = true;
1871 UI_tooltip_image_field_add(data, image_data);
1872 IMB_freeImBuf(ibuf);
1873 }
1874 }
1875}
1876
1878{
1879 if (BKE_vfont_is_builtin(&font)) {
1880 /* In memory font previews are currently not supported,
1881 * don't attempt to handle as a file. */
1882 return;
1883 }
1884 if (!font.filepath[0]) {
1885 /* These may be packed files, currently not supported. */
1886 return;
1887 }
1888
1889 char filepath_abs[FILE_MAX];
1890 STRNCPY(filepath_abs, font.filepath);
1891 BLI_path_abs(filepath_abs, ID_BLEND_PATH_FROM_GLOBAL(&font.id));
1892
1893 if (!BLI_exists(filepath_abs)) {
1895 data, TIP_("File not found"), {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_ALERT);
1896 return;
1897 }
1898
1899 float color[4];
1900 const uiWidgetColors *theme = ui_tooltip_get_theme();
1901 rgba_uchar_to_float(color, theme->text);
1902 ImBuf *ibuf = IMB_font_preview(filepath_abs, 256 * UI_SCALE_FAC, color, "ABCDabefg&0123");
1903 if (ibuf) {
1904 uiTooltipImage image_data;
1905 image_data.width = ibuf->x;
1906 image_data.height = ibuf->y;
1907 image_data.ibuf = ibuf;
1908 image_data.border = false;
1910 image_data.premultiplied = false;
1911 image_data.text_color = true;
1912 UI_tooltip_image_field_add(data, image_data);
1913 IMB_freeImBuf(ibuf);
1914 }
1915}
1916
1917static std::unique_ptr<uiTooltipData> ui_tooltip_data_from_search_item_tooltip_data(ID *id)
1918{
1919 std::unique_ptr<uiTooltipData> data = std::make_unique<uiTooltipData>();
1920 const ID_Type type_id = GS(id->name);
1921
1923
1924 if (type_id == ID_IM) {
1925 ui_tooltip_from_image(*reinterpret_cast<Image *>(id), *data);
1926 }
1927 else if (type_id == ID_MC) {
1928 ui_tooltip_from_clip(*reinterpret_cast<MovieClip *>(id), *data);
1929 }
1930 else if (type_id == ID_VF) {
1931 ui_tooltip_from_vfont(*reinterpret_cast<VFont *>(id), *data);
1932 }
1933 else {
1935 *data,
1936 fmt::format(fmt::runtime(TIP_("Choose {} data-block to be assigned to this user")),
1938 {},
1941 }
1942
1944 if (ID_IS_LINKED(id)) {
1946 fmt::format(fmt::runtime(TIP_("Source library: {}\n{}")),
1947 id->lib->id.name + 2,
1948 id->lib->filepath),
1949 {},
1952 }
1953
1954 return data->fields.is_empty() ? nullptr : std::move(data);
1955}
1956
1958 const ARegion *searchbox_region,
1959 const rcti *item_rect,
1960 ID *id)
1961{
1962 std::unique_ptr<uiTooltipData> data = ui_tooltip_data_from_search_item_tooltip_data(id);
1963 if (data == nullptr) {
1964 return nullptr;
1965 }
1966
1967 const wmWindow *win = CTX_wm_window(C);
1968 float init_position[2];
1969 init_position[0] = win->eventstate->xy[0];
1970 init_position[1] = item_rect->ymin + searchbox_region->winrct.ymin - (UI_POPUP_MARGIN / 2);
1971
1972 return ui_tooltip_create_with_data(C, std::move(data), init_position, nullptr);
1973}
1974
1975void UI_tooltip_free(bContext *C, bScreen *screen, ARegion *region)
1976{
1977 ui_region_temp_remove(C, screen, region);
1978}
1979
void immDrawPixelsTexScaledFullSize(const IMMDrawPixelsTexState *state, float x, float y, int img_w, int img_h, blender::gpu::TextureFormat gpu_format, bool use_filter, const void *rect, float scaleX, float scaleY, float xzoom, float yzoom, const float color[4])
Definition glutil.cc:51
IMMDrawPixelsTexState immDrawPixelsTexSetup(int builtin)
Definition glutil.cc:37
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:164
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 evaluating template expressions in 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:277
void BLF_size(int fontid, float size)
Definition blf.cc:443
int BLF_descender(int fontid) ATTR_WARN_UNUSED_RESULT
Definition blf.cc:872
void BLF_enable(int fontid, FontFlags flag)
Definition blf.cc:320
#define BLF_DRAW_STR_DUMMY_MAX
Definition BLF_api.hh:440
void BLF_wordwrap(int fontid, int wrap_width, BLFWrapMode mode=BLFWrapMode::Minimal)
Definition blf.cc:924
int blf_mono_font
Definition blf.cc:48
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:802
void BLF_disable(int fontid, FontFlags flag)
Definition blf.cc:329
int BLF_height_max(int fontid) ATTR_WARN_UNUSED_RESULT
Definition blf.cc:850
BLFWrapMode
Definition BLF_enums.hh:20
FontFlags
Definition BLF_enums.hh:31
@ BLF_NONE
Definition BLF_enums.hh:32
@ BLF_WORD_WRAP
Definition BLF_enums.hh:39
#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:360
#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])
MINLINE void rgb_uchar_to_float(float r_col[3], const unsigned char col_ub[3])
MINLINE void rgba_float_to_uchar(unsigned char r_col[4], const float col_f[4])
MINLINE void rgb_float_to_uchar(unsigned char r_col[3], const float col_f[3])
MINLINE void rgba_uchar_to_float(float r_col[4], const unsigned char col_ub[4])
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_pad(struct rcti *rect, int pad_x, int pad_y)
Definition rct.cc:629
void BLI_rcti_translate(struct rcti *rect, int x, int y)
Definition rct.cc:566
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
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
#define SNPRINTF_UTF8(dst, format,...)
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:694
#define ID_BLEND_PATH_FROM_GLOBAL(_id)
Definition DNA_ID.h:688
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
#define UI_SCALE_FAC
@ USER_TOOLTIPS_PYTHON
void ED_region_floating_init(ARegion *region)
Definition area.cc:2301
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:618
void immUnbindProgram()
void immBindBuiltinProgram(GPUBuiltinShader 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_ex(float x1, float y1, float x2, float y2, const float color_primary[4], const float color_secondary[4], int checker_size)
void imm_draw_box_checker_2d(float x1, float y1, float x2, float y2, bool clear_alpha=false)
@ 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(GPUBlend blend)
Definition gpu_state.cc:42
uint GPU_vertformat_attr_add(GPUVertFormat *format, blender::StringRef name, blender::gpu::VertAttrType type)
BLI_INLINE void IMB_colormanagement_srgb_to_scene_linear_v3(float scene_linear[3], const float srgb[3])
@ DISPLAY_SPACE_COLOR_INSPECTION
void IMB_colormanagement_scene_linear_to_display_v3(float pixel[3], const ColorManagedDisplay *display, const ColorManagedDisplaySpace display_space=DISPLAY_SPACE_DRAW)
BLI_INLINE void IMB_colormanagement_scene_linear_to_srgb_v3(float srgb[3], const float scene_linear[3])
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)
void IMB_rectfill_area(ImBuf *ibuf, const float scene_linear_color[4], int x1, int y1, int x2, int y2)
Definition rectop.cc:1007
bool IMB_scale(ImBuf *ibuf, unsigned int newx, unsigned int newy, IMBScaleFilter filter, bool threaded=true)
Definition scaling.cc:465
@ IB_byte_data
ImBuf * IMB_font_preview(const char *filepath, unsigned int width, const float color[4], const char *sample_text=nullptr)
Read Guarded memory(de)allocation.
@ IMB_TC_RECORD_RUN
Definition MOV_enums.hh:54
@ PROP_FLOAT
Definition RNA_types.hh:164
@ PROP_UNIT_ROTATION
Definition RNA_types.hh:178
@ PROP_PATH_SUPPORTS_BLEND_RELATIVE
Definition RNA_types.hh:456
@ PROP_ENUM_FLAG
Definition RNA_types.hh:404
@ PROP_PATH_SUPPORTS_TEMPLATES
Definition RNA_types.hh:470
PropertySubType
Definition RNA_types.hh:232
@ PROP_FILENAME
Definition RNA_types.hh:238
@ PROP_PASSWORD
Definition RNA_types.hh:243
@ PROP_NONE
Definition RNA_types.hh:233
@ PROP_DIRPATH
Definition RNA_types.hh:237
@ PROP_FILEPATH
Definition RNA_types.hh:236
#define C
Definition RandGen.cpp:29
#define UI_ALPHA_CHECKER_LIGHT
#define UI_UNIT_Y
@ UI_BLOCK_SHOW_SHORTCUT_ALWAYS
std::string UI_but_string_get_property_keymap(bContext &C, uiBut &but)
bool UI_but_has_quick_tooltip(const uiBut *but)
@ UI_BUT_REDALERT
@ UI_BUT_DISABLED
@ UI_BUT_DRIVEN
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)
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)
const ColorManagedDisplay * UI_but_cm_display_get(uiBut &but)
@ UI_BUT_NO_TOOLTIP
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)
blender::ocio::Display ColorManagedDisplay
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:278
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
nullptr float
#define str(s)
#define GS(x)
uint pos
#define round
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_to_window_rctf(const ARegion *region, const uiBlock *block, rctf *rct_dst, const rctf *rct_src)
Definition interface.cc:170
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:142
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)
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)
void UI_tooltip_uibut_python_add(uiTooltipData &data, bContext &C, uiBut &but, uiButExtraOpIcon *extra_icon)
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
void UI_tooltip_color_field_add(uiTooltipData &data, const blender::float4 &original_color, const bool has_alpha, const bool is_gamma, const ColorManagedDisplay *display, const uiTooltipColorID color_id)
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)
static std::string ui_tooltip_color_string(const blender::float4 &color, const blender::StringRefNull title, const int max_title_len, const bool show_alpha, const bool show_hex=false)
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)
int RNA_property_flag(PropertyRNA *prop)
std::string RNA_string_get(PointerRNA *ptr, const char *name)
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:414
struct Library * lib
Definition DNA_ID.h:420
char name[258]
Definition DNA_ID.h:432
int us
Definition DNA_ID.h:443
ListBase anims
ColorManagedColorspaceSettings colorspace_settings
char filepath[1024]
short source
char filepath[1024]
Definition DNA_ID.h:552
ID id
Definition DNA_ID.h:550
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:453
int width
Definition BLF_api.hh:457
char filepath[1024]
float xmax
float xmin
float ymax
float ymin
int ymin
int ymax
int xmin
int xmax
wmOperatorCallParams * optype_params
PropertyRNA * rnaprop
wmOperatorType * optype
const char * disabled_info
ButType type
void * tip_arg
uiBlock * block
PointerRNA * opptr
blender::wm::OpCallContext opcontext
uiButToolTipFunc tip_func
std::string drawstr
PointerRNA rnapoin
uiButToolTipCustomFunc tip_custom_func
uiFontStyle tooltip
blender::Vector< uiTooltipField > fields
struct uiTooltipField::@247222243334244135266242220250025345364111346003 geom
std::optional< uiTooltipImage > image
uiTooltipImageBackground background
unsigned char inner[4]
unsigned char text[4]
int xy[2]
Definition WM_types.hh:761
wmOperatorType * type
PropertyRNA * prop
wmGizmoFnScreenBoundsGet screen_bounds_get
const wmGizmoType * type
blender::Vector< wmGizmoProperty, 0 > target_properties
blender::wm::OpCallContext opcontext
Definition WM_types.hh:1167
wmOperatorType * optype
Definition WM_types.hh:1165
const char * idname
Definition WM_types.hh:1035
struct wmEvent * eventstate
i
Definition text_draw.cc:230
static const char hex[17]
Definition thumbs.cc:161
wmOperatorType * ot
Definition wm_files.cc:4237
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, blender::wm::OpCallContext 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)