Blender V5.0
node_socket_tooltip.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2025 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include <fmt/format.h>
6#include <sstream>
7
8#include "BKE_context.hh"
9#include "BKE_idtype.hh"
10#include "BKE_lib_id.hh"
11#include "BKE_node_enum.hh"
12#include "BKE_node_runtime.hh"
14
15#include "BLI_math_euler.hh"
16#include "BLI_string.h"
17
18#include "BLT_translation.hh"
19
21#include "DNA_material_types.h"
22
24#include "NOD_menu_value.hh"
26#include "NOD_socket.hh"
27
28#include "ED_node.hh"
29
30#include "node_intern.hh"
31
33
35
37 private:
38 uiTooltipData &tip_data_;
39 const bNodeTree &tree_;
40 const bNode &node_;
41 const bNodeSocket &socket_;
42 uiBut *but_ = nullptr;
43 bContext &C_;
44 int indentation_ = 0;
45
46 enum class TooltipBlockType {
47 Label,
48 Description,
49 Value,
50 Python,
51 };
52
53 std::optional<TooltipBlockType> last_block_type_;
54
55 public:
57 const bNodeTree &tree,
58 const bNodeSocket &socket,
59 bContext &C,
60 uiBut *but)
61 : tip_data_(tip_data),
62 tree_(tree),
63 node_(socket.owner_node()),
64 socket_(socket),
65 but_(but),
66 C_(C)
67 {
68 }
69
70 void build()
71 {
72 const bool is_extend = StringRef(socket_.idname) == "NodeSocketVirtual";
73 if (is_extend) {
74 this->build_tooltip_extend_socket();
75 return;
76 }
77 if (node_.is_dangling_reroute()) {
78 this->build_tooltip_dangling_reroute();
79 return;
80 }
81 if (this->should_show_label()) {
82 this->build_tooltip_label();
83 }
84 this->build_tooltip_description();
85 this->build_tooltip_value();
86 this->build_python();
87
88 /* Extra padding at the bottom. */
89 this->add_space();
90 }
91
92 private:
93 void build_tooltip_extend_socket()
94 {
95 this->add_text_field(TIP_("Connect a link to create a new socket."));
96 }
97
98 void build_tooltip_dangling_reroute()
99 {
100 this->add_text_field(TIP_("Dangling reroute nodes are ignored."), UI_TIP_LC_ALERT);
101 }
102
103 bool should_show_label()
104 {
105 if (this->get_socket_description().value_or("").empty()) {
106 /* Show label when the description is empty so that the tooltip is never empty. */
107 return true;
108 }
109 if (socket_.is_output()) {
110 return false;
111 }
112 if (socket_.type == SOCK_MENU) {
113 return true;
114 }
115 if (socket_.runtime->declaration && socket_.runtime->declaration->optional_label) {
116 return true;
117 }
118 return false;
119 }
120
121 void build_tooltip_label()
122 {
123 this->start_block(TooltipBlockType::Label);
124 if (node_.is_reroute()) {
125 this->add_text_field_header(TIP_("Reroute"));
126 return;
127 }
128 const StringRefNull translated_socket_label = node_socket_get_label(&socket_, nullptr);
129 this->add_text_field_header(translated_socket_label);
130 }
131
132 void build_tooltip_description()
133 {
134 std::optional<std::string> description_opt = this->get_socket_description();
135 if (!description_opt) {
136 return;
137 }
138 std::string description = std::move(*description_opt);
139 if (description.empty()) {
140 return;
141 }
142 if (description[description.size() - 1] != '.') {
143 description += '.';
144 }
145 this->start_block(TooltipBlockType::Description);
146 this->add_text_field(std::move(description));
147 }
148
149 std::optional<std::string> get_socket_description()
150 {
151 if (socket_.runtime->declaration == nullptr) {
152 if (socket_.description[0]) {
153 return socket_.description;
154 }
155 return std::nullopt;
156 }
157 const nodes::SocketDeclaration &socket_decl = *socket_.runtime->declaration;
158 if (!socket_decl.description.empty()) {
159 return TIP_(socket_decl.description);
160 }
161 if (socket_decl.align_with_previous_socket) {
162 const Span<nodes::ItemDeclarationPtr> all_items = node_.runtime->declaration->all_items;
163 for (const int i : all_items.index_range()) {
164 if (&*all_items[i] != &socket_decl) {
165 continue;
166 }
167 if (i == 0) {
168 break;
169 }
170 const nodes::SocketDeclaration *previous_socket_decl =
171 dynamic_cast<const nodes::SocketDeclaration *>(all_items[i - 1].get());
172 if (!previous_socket_decl) {
173 break;
174 }
175 if (!previous_socket_decl->description.empty()) {
176 return TIP_(previous_socket_decl->description);
177 }
178 }
179 }
180
181 return std::nullopt;
182 }
183
184 void build_tooltip_value()
185 {
186 SpaceNode *snode = CTX_wm_space_node(&C_);
187 geo_log::ContextualGeoTreeLogs geo_tree_logs;
188 if (snode) {
190 }
191 geo_log::GeoTreeLog *geo_tree_log = geo_tree_logs.get_main_tree_log(socket_);
192 if (geo_tree_log && this->build_tooltip_value_from_geometry_nodes_log(*geo_tree_log)) {
193 return;
194 }
195 const bool always_show_value = tree_.type == NTREE_GEOMETRY;
196 if (node_.is_reroute()) {
197 if (always_show_value) {
198 this->start_block(TooltipBlockType::Value);
199 this->build_tooltip_value_unknown();
200 }
201 return;
202 }
203 if (socket_.is_input()) {
204 if (this->is_socket_default_value_used()) {
205 this->build_tooltip_value_socket_default();
206 return;
207 }
208 }
209 if (always_show_value) {
210 this->start_block(TooltipBlockType::Value);
211 this->build_tooltip_value_unknown();
212 }
213 }
214
215 void build_tooltip_value_unknown()
216 {
217 this->add_text_field_mono(TIP_("Value: Unknown (not evaluated)"));
218 }
219
220 void build_tooltip_value_socket_default()
221 {
222 if (socket_.is_multi_input()) {
223 this->start_block(TooltipBlockType::Value);
224 this->add_text_field_mono(TIP_("Values: None"));
225 return;
226 }
227 const nodes::SocketDeclaration *socket_decl = socket_.runtime->declaration;
228 if (socket_decl && socket_decl->input_field_type == nodes::InputSocketFieldType::Implicit) {
229 this->start_block(TooltipBlockType::Value);
230 build_tooltip_value_implicit_default(socket_decl->default_input_type);
231 return;
232 }
233 if (socket_decl && socket_decl->structure_type == nodes::StructureType::Grid) {
234 this->start_block(TooltipBlockType::Value);
235 this->build_tooltip_value_and_type_oneline(TIP_("Empty Grid"), TIP_("Volume Grid"));
236 return;
237 }
238 if (socket_.typeinfo->base_cpp_type == nullptr) {
239 return;
240 }
241 const CPPType &cpp_type = *socket_.typeinfo->base_cpp_type;
242 BUFFER_FOR_CPP_TYPE_VALUE(cpp_type, socket_value);
243 socket_.typeinfo->get_base_cpp_value(socket_.default_value, socket_value);
244 BLI_SCOPED_DEFER([&]() { cpp_type.destruct(socket_value); });
245 this->start_block(TooltipBlockType::Value);
246 this->build_tooltip_value_generic({cpp_type, socket_value});
247 }
248
249 [[nodiscard]] bool build_tooltip_value_from_geometry_nodes_log(geo_log::GeoTreeLog &geo_tree_log)
250 {
251 if (socket_.typeinfo->base_cpp_type == nullptr) {
252 return false;
253 }
254 geo_tree_log.ensure_socket_values();
255 if (socket_.is_multi_input()) {
256 return this->build_tooltip_last_value_multi_input(geo_tree_log);
257 }
258 geo_log::ValueLog *value_log = geo_tree_log.find_socket_value_log(socket_);
259 if (!value_log) {
260 return false;
261 }
262 this->start_block(TooltipBlockType::Value);
263 this->build_tooltip_value_geo_log(*value_log);
264 return true;
265 }
266
267 bool build_tooltip_last_value_multi_input(geo_log::GeoTreeLog &geo_tree_log)
268 {
269 const Span<const bNodeLink *> connected_links = socket_.directly_linked_links();
270
271 Vector<std::pair<int, geo_log::ValueLog *>> value_logs;
272 bool all_value_logs_missing = true;
273 for (const int i : connected_links.index_range()) {
274 const bNodeLink &link = *connected_links[i];
275 if (!link.is_used()) {
276 continue;
277 }
278 if (!(link.flag & NODE_LINK_VALID)) {
279 continue;
280 }
281 const bNodeSocket &from_socket = *link.fromsock;
282 geo_log::ValueLog *value_log = geo_tree_log.find_socket_value_log(from_socket);
283 value_logs.append({i, value_log});
284 if (value_log) {
285 all_value_logs_missing = false;
286 }
287 }
288 if (all_value_logs_missing) {
289 return false;
290 }
291
292 this->start_block(TooltipBlockType::Value);
293 for (const auto &[i, value_log] : value_logs) {
294 const int connection_number = i + 1;
295 if (i > 0) {
296 this->add_space();
297 }
298 this->add_text_field_mono(fmt::format("{}:", connection_number));
299 this->add_space();
300 indentation_++;
301 BLI_SCOPED_DEFER([&]() { indentation_--; });
302 if (value_log) {
303 this->build_tooltip_value_geo_log(*value_log);
304 }
305 else {
306 this->build_tooltip_value_unknown();
307 }
308 }
309
310 return true;
311 }
312
313 void build_tooltip_value_geo_log(geo_log::ValueLog &value_log)
314 {
315 if (const auto *generic_value_log = dynamic_cast<const geo_log::GenericValueLog *>(&value_log))
316 {
317 this->build_tooltip_value_generic(generic_value_log->value);
318 }
319 else if (const auto *string_value_log = dynamic_cast<const geo_log::StringLog *>(&value_log)) {
320 this->build_tooltip_value_string_log(*string_value_log);
321 }
322 else if (const auto *field_value_log = dynamic_cast<const geo_log::FieldInfoLog *>(&value_log))
323 {
324 this->build_tooltip_value_field_log(*field_value_log);
325 }
326 else if (const auto *geometry_log = dynamic_cast<const geo_log::GeometryInfoLog *>(&value_log))
327 {
328 this->build_tooltip_value_geometry_log(*geometry_log);
329 }
330 else if (const auto *grid_log = dynamic_cast<const geo_log::GridInfoLog *>(&value_log)) {
331 build_tooltip_value_grid_log(*grid_log);
332 }
333 else if (const auto *bundle_log = dynamic_cast<const geo_log::BundleValueLog *>(&value_log)) {
334 this->build_tooltip_value_bundle_log(*bundle_log);
335 }
336 else if (const auto *closure_log = dynamic_cast<const geo_log::ClosureValueLog *>(&value_log))
337 {
338 this->build_tooltip_value_closure_log(*closure_log);
339 }
340 else if (const auto *list_log = dynamic_cast<const geo_log::ListInfoLog *>(&value_log)) {
341 this->build_tooltip_value_list_log(*list_log);
342 }
343 }
344
345 void build_tooltip_value_and_type_oneline(const StringRef value, const StringRef type)
346 {
347 this->add_text_field_mono(fmt::format("{}: {}", TIP_("Value"), value));
348 this->add_space();
349 this->add_text_field_mono(fmt::format("{}: {}", TIP_("Type"), type));
350 }
351
352 template<typename T> [[nodiscard]] bool build_tooltip_value_data_block(const GPointer &value)
353 {
354 const CPPType &type = *value.type();
355 if (!type.is<T *>()) {
356 return false;
357 }
358 const T *data = *value.get<T *>();
359 std::string value_str;
360 if (data) {
361 value_str = BKE_id_name(id_cast<const ID &>(*data));
362 }
363 else {
364 value_str = TIP_("None");
365 }
366 const ID_Type id_type = T::id_type;
367 const char *id_type_name = BKE_idtype_idcode_to_name(id_type);
368
369 this->build_tooltip_value_and_type_oneline(value_str, TIP_(id_type_name));
370 return true;
371 }
372
373 void build_tooltip_value_enum(const nodes::MenuValue menu_item)
374 {
375 const auto *storage = socket_.default_value_typed<bNodeSocketValueMenu>();
376 if (!storage->enum_items || storage->has_conflict()) {
377 this->build_tooltip_value_and_type_oneline(TIP_("Unknown"), TIP_("Menu"));
378 return;
379 }
380 const bke::RuntimeNodeEnumItem *enum_item = storage->enum_items->find_item_by_identifier(
381 menu_item.value);
382 if (!enum_item) {
383 return;
384 }
385 if (!enum_item->description.empty()) {
386 this->add_text_field(TIP_(enum_item->description), UI_TIP_LC_VALUE);
387 this->add_space();
388 }
389 this->build_tooltip_value_and_type_oneline(TIP_(enum_item->name), TIP_("Menu"));
390 }
391
392 void build_tooltip_value_int(const int value)
393 {
394 std::string value_str = fmt::format("{}", value);
395 this->build_tooltip_value_and_type_oneline(value_str, TIP_("Integer"));
396 }
397
398 void build_tooltip_value_float(const float value)
399 {
400 std::string value_str;
401 /* Above that threshold floats can't represent fractions anymore. */
402 if (std::abs(value) > (1 << 24)) {
403 /* Use higher precision to display correct integer value instead of one that is rounded to
404 * fewer significant digits. */
405 value_str = fmt::format("{:.10}", value);
406 }
407 else {
408 value_str = fmt::format("{}", value);
409 }
410 this->build_tooltip_value_and_type_oneline(value_str, TIP_("Float"));
411 }
412
413 void build_tooltip_value_float3(const float3 &value)
414 {
415 const std::string value_str = fmt::format("{} {} {}", value.x, value.y, value.z);
416 this->build_tooltip_value_and_type_oneline(value_str, TIP_("3D Float Vector"));
417 }
418
419 void build_tooltip_value_color(const ColorGeometry4f &value)
420 {
421 const std::string value_str = fmt::format(
422 "{} {} {} {} ({})", value.r, value.g, value.b, value.a, TIP_("Linear"));
423 this->build_tooltip_value_and_type_oneline(value_str, TIP_("Float Color"));
424 this->add_space();
425
426 bool is_gamma = false;
427 const ColorManagedDisplay *display = nullptr;
428 if (but_) {
429 is_gamma = UI_but_is_color_gamma(*but_);
430 display = UI_but_cm_display_get(*but_);
431 }
432 UI_tooltip_color_field_add(tip_data_, float4(value), true, is_gamma, display, UI_TIP_LC_VALUE);
433 }
434
435 void build_tooltip_value_quaternion(const math::Quaternion &value)
436 {
437 const math::EulerXYZ euler = math::to_euler(value);
438 const std::string value_str = fmt::format("{}" BLI_STR_UTF8_DEGREE_SIGN
441 euler.x().degree(),
442 euler.y().degree(),
443 euler.z().degree());
444 this->build_tooltip_value_and_type_oneline(value_str, TIP_("Rotation"));
445 }
446
447 void build_tooltip_value_bool(const bool value)
448 {
449 std::string value_str = value ? TIP_("True") : TIP_("False");
450 this->build_tooltip_value_and_type_oneline(value_str, TIP_("Boolean"));
451 }
452
453 void build_tooltip_value_float4x4(const float4x4 &value)
454 {
455 /* Transpose to be able to print row by row. */
456 const float4x4 value_transposed = math::transpose(value);
457
458 std::stringstream ss;
459 for (const int row_i : IndexRange(4)) {
460 const float4 row = value_transposed[row_i];
461 ss << fmt::format("{:7.3} {:7.3} {:7.3} {:7.3}\n", row[0], row[1], row[2], row[3]);
462 }
463
464 this->add_text_field_mono(fmt::format("{}:", TIP_("Value")));
465 this->add_space();
466 this->add_text_field_mono(ss.str());
467 this->add_space();
468 this->add_text_field_mono(fmt::format("{}: {}", TIP_("Type"), TIP_("4x4 Float Matrix")));
469 }
470
471 void build_tooltip_value_generic(const GPointer &value)
472
473 {
474 const CPPType &value_type = *value.type();
475 if (this->build_tooltip_value_data_block<Object>(value)) {
476 return;
477 }
478 if (this->build_tooltip_value_data_block<Material>(value)) {
479 return;
480 }
481 if (this->build_tooltip_value_data_block<Tex>(value)) {
482 return;
483 }
484 if (this->build_tooltip_value_data_block<Image>(value)) {
485 return;
486 }
487 if (this->build_tooltip_value_data_block<Collection>(value)) {
488 return;
489 }
490
491 if (socket_.type == SOCK_MENU) {
492 if (!value_type.is<nodes::MenuValue>()) {
493 this->build_tooltip_value_unknown();
494 return;
495 }
496 const nodes::MenuValue menu_item = *value.get<nodes::MenuValue>();
497 this->build_tooltip_value_enum(menu_item);
498 return;
499 }
500
501 const CPPType &socket_base_cpp_type = *socket_.typeinfo->base_cpp_type;
502 const bke::DataTypeConversions &conversions = bke::get_implicit_type_conversions();
503 if (value_type != socket_base_cpp_type) {
504 if (!conversions.is_convertible(value_type, socket_base_cpp_type)) {
505 this->build_tooltip_value_unknown();
506 return;
507 }
508 }
509 BUFFER_FOR_CPP_TYPE_VALUE(socket_base_cpp_type, socket_value);
510 conversions.convert_to_uninitialized(
511 value_type, socket_base_cpp_type, value.get(), socket_value);
512 BLI_SCOPED_DEFER([&]() { socket_base_cpp_type.destruct(socket_value); });
513
514 if (socket_base_cpp_type.is<int>()) {
515 this->build_tooltip_value_int(*static_cast<int *>(socket_value));
516 return;
517 }
518 if (socket_base_cpp_type.is<float>()) {
519 this->build_tooltip_value_float(*static_cast<float *>(socket_value));
520 return;
521 }
522 if (socket_base_cpp_type.is<float3>()) {
523 this->build_tooltip_value_float3(*static_cast<float3 *>(socket_value));
524 return;
525 }
526 if (socket_base_cpp_type.is<ColorGeometry4f>()) {
527 this->build_tooltip_value_color(*static_cast<ColorGeometry4f *>(socket_value));
528 return;
529 }
530 if (socket_base_cpp_type.is<math::Quaternion>()) {
531 this->build_tooltip_value_quaternion(*static_cast<math::Quaternion *>(socket_value));
532 return;
533 }
534 if (socket_base_cpp_type.is<bool>()) {
535 this->build_tooltip_value_bool(*static_cast<bool *>(socket_value));
536 return;
537 }
538 if (socket_base_cpp_type.is<float4x4>()) {
539 this->build_tooltip_value_float4x4(*static_cast<float4x4 *>(socket_value));
540 return;
541 }
542 this->build_tooltip_value_unknown();
543 }
544
545 void build_tooltip_value_string_log(const geo_log::StringLog &value_log)
546 {
547 std::string value_str = value_log.value;
548 if (value_log.truncated) {
549 value_str += "...";
550 }
551 this->build_tooltip_value_and_type_oneline(value_str, TIP_("String"));
552 }
553
554 const char *get_field_type_name(const CPPType &base_type)
555 {
556 if (base_type.is<int>()) {
557 return TIP_("Integer Field");
558 }
559 if (base_type.is<float>()) {
560 return TIP_("Float Field");
561 }
562 if (base_type.is<blender::float3>()) {
563 return TIP_("3D Float Vector Field");
564 }
565 if (base_type.is<bool>()) {
566 return TIP_("Boolean Field");
567 }
568 if (base_type.is<std::string>()) {
569 return TIP_("String Field");
570 }
571 if (base_type.is<blender::ColorGeometry4f>()) {
572 return TIP_("Color Field");
573 }
574 if (base_type.is<math::Quaternion>()) {
575 return TIP_("Rotation Field");
576 }
577 if (base_type.is<blender::float4x4>()) {
578 return TIP_("Matrix Field");
579 }
581 return TIP_("Field");
582 }
583
584 void build_tooltip_value_field_log(const geo_log::FieldInfoLog &value_log)
585 {
586 const CPPType &socket_base_cpp_type = *socket_.typeinfo->base_cpp_type;
587 const Span<std::string> input_tooltips = value_log.input_tooltips;
588
589 if (input_tooltips.is_empty()) {
590 /* Should have been logged as constant value. */
592 return;
593 }
594
595 this->add_text_field_mono(TIP_("Field depending on:"));
596
597 for (const std::string &input_tooltip : input_tooltips) {
598 this->add_space();
599 this->add_text_field_mono(fmt::format(" \u2022 {}", input_tooltip));
600 }
601
602 this->add_space();
603 std::string type_str = this->get_field_type_name(socket_base_cpp_type);
604 this->add_text_field_mono(fmt::format("{}: {}", TIP_("Type"), type_str));
605 }
606
607 std::string count_to_string(const int count)
608 {
611 return std::string(str);
612 }
613
614 void build_tooltip_value_geometry_log(const geo_log::GeometryInfoLog &geometry_log)
615 {
616 Span<bke::GeometryComponent::Type> component_types = geometry_log.component_types;
617 if (component_types.is_empty()) {
618 this->build_tooltip_value_and_type_oneline(TIP_("None"), TIP_("Geometry Set"));
619 return;
620 }
621 this->add_text_field_mono(TIP_("Geometry components:"));
622 for (const bke::GeometryComponent::Type type : component_types) {
623 std::string component_str;
624 switch (type) {
626 const geo_log::GeometryInfoLog::MeshInfo &info = *geometry_log.mesh_info;
627 component_str = fmt::format(fmt::runtime(TIP_("Mesh: {} vertices, {} edges, {} faces")),
628 this->count_to_string(info.verts_num),
629 this->count_to_string(info.edges_num),
630 this->count_to_string(info.faces_num));
631 break;
632 }
634 const geo_log::GeometryInfoLog::PointCloudInfo &info = *geometry_log.pointcloud_info;
635 component_str = fmt::format(fmt::runtime(TIP_("Point Cloud: {} points")),
636 this->count_to_string(info.points_num));
637 break;
638 }
640 const geo_log::GeometryInfoLog::InstancesInfo &info = *geometry_log.instances_info;
641 component_str = fmt::format(fmt::runtime(TIP_("Instances: {}")),
642 this->count_to_string(info.instances_num));
643 break;
644 }
646 const geo_log::GeometryInfoLog::VolumeInfo &info = *geometry_log.volume_info;
647 component_str = fmt::format(fmt::runtime(TIP_("Volume: {} grids")),
648 this->count_to_string(info.grids.size()));
649 break;
650 }
652 const geo_log::GeometryInfoLog::CurveInfo &info = *geometry_log.curve_info;
653 component_str = fmt::format(fmt::runtime(TIP_("Curve: {} points, {} splines")),
654 this->count_to_string(info.points_num),
655 this->count_to_string(info.splines_num));
656 break;
657 }
659 const geo_log::GeometryInfoLog::GreasePencilInfo &info =
660 *geometry_log.grease_pencil_info;
661 component_str = fmt::format(fmt::runtime(TIP_("Grease Pencil: {} layers")),
662 this->count_to_string(info.layers_num));
663 break;
664 }
666 if (geometry_log.edit_data_info.has_value()) {
667 const geo_log::GeometryInfoLog::EditDataInfo &info = *geometry_log.edit_data_info;
668 component_str = fmt::format(
669 fmt::runtime(TIP_("Edit: {}, {}, {}")),
670 info.has_deformed_positions ? TIP_("positions") : TIP_("no positions"),
671 info.has_deform_matrices ? TIP_("matrices") : TIP_("no matrices"),
672 info.gizmo_transforms_num > 0 ? TIP_("gizmos") : TIP_("no gizmos"));
673 }
674 break;
675 }
676 }
677 if (!component_str.empty()) {
678 this->add_space();
679 this->add_text_field_mono(fmt::format(" \u2022 {}", component_str));
680 }
681 }
682 this->add_space();
683 this->add_text_field_mono(TIP_("Type: Geometry Set"));
684 }
685
686 void build_tooltip_value_grid_log(const geo_log::GridInfoLog &grid_log)
687 {
688 std::string value_str;
689 if (grid_log.is_empty) {
690 value_str = TIP_("None");
691 }
692 else {
693 value_str = TIP_("Volume Grid");
694 }
695 this->build_tooltip_value_and_type_oneline(value_str, TIP_("Volume Grid"));
696 }
697
698 void build_tooltip_value_bundle_log(const geo_log::BundleValueLog &bundle_log)
699 {
700 if (bundle_log.items.is_empty()) {
701 this->add_text_field_mono(TIP_("Values: None"));
702 }
703 else {
704 this->add_text_field_mono(TIP_("Values:"));
705 Vector<geo_log::BundleValueLog::Item> sorted_items = bundle_log.items;
706 std::sort(sorted_items.begin(), sorted_items.end(), [](const auto &a, const auto &b) {
707 return BLI_strcasecmp_natural(a.key.c_str(), b.key.c_str()) < 0;
708 });
709 for (const geo_log::BundleValueLog::Item &item : sorted_items) {
710 this->add_space();
711 std::string type_name;
712 if (const bke::bNodeSocketType *const *socket_type =
713 std::get_if<const bke::bNodeSocketType *>(&item.type))
714 {
715 type_name = TIP_((*socket_type)->label);
716 }
717 else if (const StringRefNull *internal_type_name = std::get_if<StringRefNull>(&item.type))
718 {
719 type_name = *internal_type_name;
720 }
721 this->add_text_field_mono(
722 fmt::format(fmt::runtime("\u2022 \"{}\" ({})\n"), item.key, type_name));
723 }
724 }
725 this->add_space();
726 this->add_text_field_mono(TIP_("Type: Bundle"));
727 }
728
729 void build_tooltip_value_closure_log(const geo_log::ClosureValueLog &closure_log)
730 {
731 if (closure_log.inputs.is_empty() && closure_log.outputs.is_empty()) {
732 this->add_text_field_mono(TIP_("Value: None"));
733 }
734 else {
735 if (!closure_log.inputs.is_empty()) {
736 this->add_text_field_mono(TIP_("Inputs:"));
737 for (const geo_log::ClosureValueLog::Item &item : closure_log.inputs) {
738 this->add_space();
739 const std::string type_name = TIP_(item.type->label);
740 this->add_text_field_mono(
741 fmt::format(fmt::runtime("\u2022 \"{}\" ({})\n"), item.key, type_name));
742 }
743 }
744 if (!closure_log.outputs.is_empty()) {
745 this->add_space();
746 this->add_text_field_mono(TIP_("Outputs:"));
747 for (const geo_log::ClosureValueLog::Item &item : closure_log.outputs) {
748 this->add_space();
749 const std::string type_name = TIP_(item.type->label);
750 this->add_text_field_mono(
751 fmt::format(fmt::runtime("\u2022 \"{}\" ({})\n"), item.key, type_name));
752 }
753 }
754 }
755 this->add_space();
756 this->add_text_field_mono(TIP_("Type: Closure"));
757 }
758
759 void build_tooltip_value_list_log(const geo_log::ListInfoLog &list_log)
760 {
761 this->add_text_field_mono(fmt::format("{}: {}", TIP_("Length"), list_log.size));
762 this->add_space();
763 this->add_text_field_mono(TIP_("Type: List"));
764 }
765
766 void build_tooltip_value_implicit_default(const NodeDefaultInputType &type)
767 {
768 switch (type) {
770 /* Should be handled elsewhere. */
772 break;
773 }
775 this->build_tooltip_value_and_type_oneline(TIP_("Index Field"),
776 this->get_field_type_name(CPPType::get<int>()));
777 break;
778 }
780 this->build_tooltip_value_and_type_oneline(TIP_("ID or Index Field"),
781 this->get_field_type_name(CPPType::get<int>()));
782 break;
783 }
785 this->build_tooltip_value_and_type_oneline(
786 TIP_("Normal Field"), this->get_field_type_name(CPPType::get<float3>()));
787 break;
788 }
790 this->build_tooltip_value_and_type_oneline(
791 TIP_("Position Field"), this->get_field_type_name(CPPType::get<float3>()));
792 break;
793 }
795 this->build_tooltip_value_and_type_oneline(
796 TIP_("Instance Transform Field"), this->get_field_type_name(CPPType::get<float4x4>()));
797 break;
798 }
800 this->build_tooltip_value_and_type_oneline(
801 TIP_("Left Handle Field"), this->get_field_type_name(CPPType::get<float3>()));
802 break;
803 }
805 this->build_tooltip_value_and_type_oneline(
806 TIP_("Right Handle Field"), this->get_field_type_name(CPPType::get<float3>()));
807 break;
808 }
809 }
810
811 bool is_socket_default_value_used()
812 {
813 BLI_assert(socket_.is_input());
814 for (const bNodeLink *link : socket_.directly_linked_links()) {
815 if (!link->is_used()) {
816 continue;
817 }
818 const bNodeSocket &from_socket = *link->fromsock;
819 const bNode &from_node = from_socket.owner_node();
820 if (from_node.is_dangling_reroute()) {
821 continue;
822 }
823 return false;
824 }
825 return true;
826 }
827
828 StringRef get_structure_type_tooltip(const nodes::StructureType &structure_type)
829 {
830 switch (structure_type) {
831 case nodes::StructureType::Single: {
832 return TIP_("Single Value");
833 }
834 case nodes::StructureType::Dynamic: {
835 return TIP_("Dynamic");
836 }
837 case nodes::StructureType::Field: {
838 return TIP_("Field");
839 }
840 case nodes::StructureType::Grid: {
841 return TIP_("Volume Grid");
842 }
843 case nodes::StructureType::List: {
844 return TIP_("List");
845 }
846 }
848 return "Unknown";
849 }
850
851 void build_python()
852 {
853 if (!(U.flag & USER_TOOLTIPS_PYTHON)) {
854 return;
855 }
856 if (!but_) {
857 return;
858 }
859 UI_tooltip_uibut_python_add(tip_data_, C_, *but_, nullptr);
860 }
861
862 void start_block(const TooltipBlockType new_block_type)
863 {
864 if (last_block_type_.has_value()) {
865 this->add_space(2);
866 }
867 last_block_type_ = new_block_type;
868 }
869
870 void add_text_field_header(std::string text)
871 {
873 tip_data_, this->indent(text), {}, UI_TIP_STYLE_HEADER, UI_TIP_LC_MAIN);
874 }
875
876 void add_text_field(std::string text, const uiTooltipColorID color_id = UI_TIP_LC_NORMAL)
877 {
878 UI_tooltip_text_field_add(tip_data_, this->indent(text), {}, UI_TIP_STYLE_NORMAL, color_id);
879 }
880
881 void add_text_field_mono(std::string text, const uiTooltipColorID color_id = UI_TIP_LC_VALUE)
882 {
883 UI_tooltip_text_field_add(tip_data_, this->indent(text), {}, UI_TIP_STYLE_MONO, color_id);
884 }
885
886 void add_space(const int amount = 1)
887 {
888 for ([[maybe_unused]] const int i : IndexRange(amount)) {
890 }
891 }
892
893 std::string indent(std::string text)
894 {
895 if (indentation_ == 0) {
896 return text;
897 }
898 return fmt::format("{: <{}}{}", "", indentation_, text);
899 }
900};
901
903 bContext &C,
904 uiBut *but,
905 const bNodeTree &tree,
906 const bNodeSocket &socket)
907{
908 SocketTooltipBuilder builder(tip_data, tree, socket, C, but);
909 builder.build();
910}
911
912} // namespace blender::ed::space_node
SpaceNode * CTX_wm_space_node(const bContext *C)
const char * BKE_idtype_idcode_to_name(short idcode)
Definition idtype.cc:164
const char * BKE_id_name(const ID &id)
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BUFFER_FOR_CPP_TYPE_VALUE(type, variable_name)
#define BLI_SCOPED_DEFER(function_to_defer)
size_t BLI_str_format_int_grouped(char dst[BLI_STR_FORMAT_INT32_GROUPED_SIZE], int num) ATTR_NONNULL(1)
Definition string.cc:1171
#define BLI_STR_FORMAT_INT32_GROUPED_SIZE
Definition BLI_string.h:28
#define BLI_STR_UTF8_DEGREE_SIGN
#define TIP_(msgid)
ID_Type
Object groups, one object can be in many groups at once.
@ NODE_DEFAULT_INPUT_POSITION_FIELD
@ NODE_DEFAULT_INPUT_HANDLE_RIGHT_FIELD
@ NODE_DEFAULT_INPUT_HANDLE_LEFT_FIELD
@ NODE_DEFAULT_INPUT_ID_INDEX_FIELD
@ NODE_DEFAULT_INPUT_INSTANCE_TRANSFORM_FIELD
@ NODE_DEFAULT_INPUT_NORMAL_FIELD
struct bNodeSocketValueMenu bNodeSocketValueMenu
@ NTREE_GEOMETRY
struct bNodeLink bNodeLink
@ NODE_LINK_VALID
struct bNode bNode
@ SOCK_MENU
struct bNodeSocket bNodeSocket
struct SpaceNode SpaceNode
@ USER_TOOLTIPS_PYTHON
#define C
Definition RandGen.cpp:29
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=false)
void UI_tooltip_uibut_python_add(uiTooltipData &data, bContext &C, uiBut &but, uiButExtraOpIcon *extra_icon)
@ UI_TIP_STYLE_MONO
@ UI_TIP_STYLE_NORMAL
@ UI_TIP_STYLE_SPACER
@ UI_TIP_STYLE_HEADER
uiTooltipColorID
@ UI_TIP_LC_ALERT
@ UI_TIP_LC_MAIN
@ UI_TIP_LC_VALUE
@ UI_TIP_LC_NORMAL
void UI_tooltip_color_field_add(uiTooltipData &data, const blender::float4 &color, bool has_alpha, bool is_gamma, const ColorManagedDisplay *display, uiTooltipColorID color_id)
const ColorManagedDisplay * UI_but_cm_display_get(uiBut &but)
bool UI_but_is_color_gamma(uiBut &but)
blender::ocio::Display ColorManagedDisplay
#define U
bool is() const
void destruct(void *ptr) const
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr bool is_empty() const
Definition BLI_span.hh:260
void append(const T &value)
bool is_empty() const
T * end()
T * begin()
static const CPPType & get()
SocketTooltipBuilder(uiTooltipData &tip_data, const bNodeTree &tree, const bNodeSocket &socket, bContext &C, uiBut *but)
GeoTreeLog * get_main_tree_log(const bke::bNodeTreeZone *zone) const
static ContextualGeoTreeLogs get_contextual_tree_logs(const SpaceNode &snode)
ValueLog * find_socket_value_log(const bNodeSocket &query_socket)
std::optional< GreasePencilInfo > grease_pencil_info
Vector< bke::GeometryComponent::Type > component_types
KDTree_3d * tree
#define str(s)
#define T
const DataTypeConversions & get_implicit_type_conversions()
NodeLinkData data[NODELINK_GROUP_SIZE]
Definition drawnode.cc:1863
const char * node_socket_get_label(const bNodeSocket *socket, const char *panel_label=nullptr)
Definition node_draw.cc:453
void build_socket_tooltip(uiTooltipData &tip_data, bContext &C, uiBut *but, const bNodeTree &tree, const bNodeSocket &socket)
QuaternionBase< float > Quaternion
MatBase< T, NumCol, NumRow > transpose(const MatBase< T, NumRow, NumCol > &mat)
EulerXYZBase< float > EulerXYZ
Euler3Base< T > to_euler(const AxisAngleBase< T, AngleT > &axis_angle, EulerOrder order)
MatBase< float, 4, 4 > float4x4
VecBase< float, 4 > float4
ColorSceneLinear4f< eAlpha::Premultiplied > ColorGeometry4f
VecBase< float, 3 > float3
const RuntimeNodeEnumItemsHandle * enum_items
float z
Definition sky_math.h:136
float y
Definition sky_math.h:136
float x
Definition sky_math.h:136
i
Definition text_draw.cc:230