Blender V4.5
spreadsheet_layout.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include <iomanip>
6#include <sstream>
7
8#include <fmt/format.h>
9
10#include "BLF_api.hh"
11
12#include "BLI_math_matrix.hh"
15
16#include "BKE_instances.hh"
17
20#include "spreadsheet_layout.hh"
21
22#include "DNA_meshdata_types.h"
23
24#include "UI_interface.hh"
25#include "UI_resources.hh"
26
27#include "BLT_translation.hh"
28
30
32 private:
33 const SpreadsheetLayout &spreadsheet_layout_;
34
35 public:
36 SpreadsheetLayoutDrawer(const SpreadsheetLayout &spreadsheet_layout)
37 : spreadsheet_layout_(spreadsheet_layout)
38 {
39 tot_columns = spreadsheet_layout.columns.size();
40 tot_rows = spreadsheet_layout.row_indices.size();
41 left_column_width = spreadsheet_layout.index_column_width;
42 }
43
44 void draw_top_row_cell(int column_index, const CellDrawParams &params) const final
45 {
46 const StringRefNull name = spreadsheet_layout_.columns[column_index].values->name();
47 uiBut *but = uiDefIconTextBut(params.block,
49 0,
50 ICON_NONE,
51 name,
52 params.xmin,
53 params.ymin,
54 params.width,
55 params.height,
56 nullptr,
57 0,
58 0,
59 std::nullopt);
61 but,
62 [](bContext * /*C*/, void *arg, blender::StringRef /*tip*/) {
63 return *static_cast<std::string *>(arg);
64 },
65 MEM_new<std::string>(__func__, name),
66 [](void *arg) { MEM_delete(static_cast<std::string *>(arg)); });
67 /* Center-align column headers. */
70 }
71
72 void draw_left_column_cell(int row_index, const CellDrawParams &params) const final
73 {
74 const int real_index = spreadsheet_layout_.row_indices[row_index];
75 std::string index_str = std::to_string(real_index);
76 uiBut *but = uiDefIconTextBut(params.block,
78 0,
79 ICON_NONE,
80 index_str,
81 params.xmin,
82 params.ymin,
83 params.width,
84 params.height,
85 nullptr,
86 0,
87 0,
88 std::nullopt);
89 /* Right-align indices. */
92 }
93
94 void draw_content_cell(int row_index, int column_index, const CellDrawParams &params) const final
95 {
96 const int real_index = spreadsheet_layout_.row_indices[row_index];
97 const ColumnValues &column = *spreadsheet_layout_.columns[column_index].values;
98 if (real_index > column.size()) {
99 return;
100 }
101
102 const GVArray &data = column.data();
103
104 if (data.type().is<int>()) {
105 const int value = data.get<int>(real_index);
106 const std::string value_str = std::to_string(value);
107 uiBut *but = uiDefIconTextBut(params.block,
109 0,
110 ICON_NONE,
111 value_str,
112 params.xmin,
113 params.ymin,
114 params.width,
115 params.height,
116 nullptr,
117 0,
118 0,
119 std::nullopt);
121 but,
122 [](bContext * /*C*/, void *argN, const StringRef /*tip*/) {
123 return fmt::format("{}", *((int *)argN));
124 },
125 MEM_dupallocN<int>(__func__, value),
126 MEM_freeN);
127 /* Right-align Integers. */
130 }
131 if (data.type().is<int8_t>()) {
132 const int8_t value = data.get<int8_t>(real_index);
133 const std::string value_str = std::to_string(value);
134 uiBut *but = uiDefIconTextBut(params.block,
136 0,
137 ICON_NONE,
138 value_str,
139 params.xmin,
140 params.ymin,
141 params.width,
142 params.height,
143 nullptr,
144 0,
145 0,
146 std::nullopt);
147 /* Right-align Integers. */
150 }
151 else if (data.type().is<short2>()) {
152 const int2 value = int2(data.get<short2>(real_index));
153 this->draw_int_vector(params, Span(&value.x, 2));
154 }
155 else if (data.type().is<int2>()) {
156 const int2 value = data.get<int2>(real_index);
157 this->draw_int_vector(params, Span(&value.x, 2));
158 }
159 else if (data.type().is<float>()) {
160 const float value = data.get<float>(real_index);
161 std::stringstream ss;
162 ss << std::fixed << std::setprecision(3) << value;
163 const std::string value_str = ss.str();
164 uiBut *but = uiDefIconTextBut(params.block,
166 0,
167 ICON_NONE,
168 value_str,
169 params.xmin,
170 params.ymin,
171 params.width,
172 params.height,
173 nullptr,
174 0,
175 0,
176 std::nullopt);
178 but,
179 [](bContext * /*C*/, void *argN, const StringRef /*tip*/) {
180 return fmt::format("{:f}", *((float *)argN));
181 },
182 MEM_dupallocN<float>(__func__, value),
183 MEM_freeN);
184 /* Right-align Floats. */
187 }
188 else if (data.type().is<bool>()) {
189 const bool value = data.get<bool>(real_index);
190 const int icon = value ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT;
191 uiBut *but = uiDefIconTextBut(params.block,
193 0,
194 icon,
195 "",
196 params.xmin,
197 params.ymin,
198 params.width,
199 params.height,
200 nullptr,
201 0,
202 0,
203 std::nullopt);
205 }
206 else if (data.type().is<float2>()) {
207 const float2 value = data.get<float2>(real_index);
208 this->draw_float_vector(params, Span(&value.x, 2));
209 }
210 else if (data.type().is<float3>()) {
211 const float3 value = data.get<float3>(real_index);
212 this->draw_float_vector(params, Span(&value.x, 3));
213 }
214 else if (data.type().is<ColorGeometry4f>()) {
215 const ColorGeometry4f value = data.get<ColorGeometry4f>(real_index);
216 this->draw_float_vector(params, Span(&value.r, 4));
217 }
218 else if (data.type().is<ColorGeometry4b>()) {
219 const ColorGeometry4b value = data.get<ColorGeometry4b>(real_index);
220 this->draw_byte_color(params, value);
221 }
222 else if (data.type().is<math::Quaternion>()) {
223 const float4 value = float4(data.get<math::Quaternion>(real_index));
224 this->draw_float_vector(params, Span(&value.x, 4));
225 }
226 else if (data.type().is<float4x4>()) {
227 this->draw_float4x4(params, data.get<float4x4>(real_index));
228 }
229 else if (data.type().is<bke::InstanceReference>()) {
230 const bke::InstanceReference value = data.get<bke::InstanceReference>(real_index);
231 const StringRefNull name = value.name().is_empty() ? IFACE_("(Geometry)") : value.name();
232 const int icon = get_instance_reference_icon(value);
235 0,
236 icon,
237 name.c_str(),
238 params.xmin,
239 params.ymin,
240 params.width,
241 params.height,
242 nullptr,
243 0,
244 0,
245 std::nullopt);
246 }
247 else if (data.type().is<std::string>()) {
250 0,
251 ICON_NONE,
252 data.get<std::string>(real_index),
253 params.xmin,
254 params.ymin,
255 params.width,
256 params.height,
257 nullptr,
258 0,
259 0,
260 std::nullopt);
261 }
262 else if (data.type().is<MStringProperty>()) {
264 data.get_to_uninitialized(real_index, prop);
265 uiBut *but = uiDefIconTextBut(params.block,
267 0,
268 ICON_NONE,
269 StringRef(prop->s, prop->s_len),
270 params.xmin,
271 params.ymin,
272 params.width,
273 params.height,
274 nullptr,
275 0,
276 0,
277 std::nullopt);
278
280 but,
281 [](bContext * /*C*/, void *argN, const StringRef /*tip*/) {
282 const MStringProperty &prop = *static_cast<MStringProperty *>(argN);
283 return std::string(StringRef(prop.s, prop.s_len));
284 },
285 prop,
286 MEM_freeN);
287 }
288 }
289
290 void draw_float_vector(const CellDrawParams &params, const Span<float> values) const
291 {
292 BLI_assert(!values.is_empty());
293 const float segment_width = float(params.width) / values.size();
294 for (const int i : values.index_range()) {
295 std::stringstream ss;
296 const float value = values[i];
297 ss << " " << std::fixed << std::setprecision(3) << value;
298 const std::string value_str = ss.str();
299 uiBut *but = uiDefIconTextBut(params.block,
301 0,
302 ICON_NONE,
303 value_str,
304 params.xmin + i * segment_width,
305 params.ymin,
306 segment_width,
307 params.height,
308 nullptr,
309 0,
310 0,
311 std::nullopt);
312
314 but,
315 [](bContext * /*C*/, void *argN, const StringRef /*tip*/) {
316 return fmt::format("{:f}", *((float *)argN));
317 },
318 MEM_dupallocN<float>(__func__, value),
319 MEM_freeN);
320 /* Right-align Floats. */
323 }
324 }
325
326 void draw_int_vector(const CellDrawParams &params, const Span<int> values) const
327 {
328 BLI_assert(!values.is_empty());
329 const float segment_width = float(params.width) / values.size();
330 for (const int i : values.index_range()) {
331 std::stringstream ss;
332 const int value = values[i];
333 ss << " " << value;
334 const std::string value_str = ss.str();
335 uiBut *but = uiDefIconTextBut(params.block,
337 0,
338 ICON_NONE,
339 value_str,
340 params.xmin + i * segment_width,
341 params.ymin,
342 segment_width,
343 params.height,
344 nullptr,
345 0,
346 0,
347 std::nullopt);
349 but,
350 [](bContext * /*C*/, void *argN, const StringRef /*tip*/) {
351 return fmt::format("{}", *((int *)argN));
352 },
353 MEM_dupallocN<int>(__func__, value),
354 MEM_freeN);
355 /* Right-align Floats. */
358 }
359 }
360
362 {
363 const ColorGeometry4f float_color = color.decode();
364 Span<float> values(&float_color.r, 4);
365 const float segment_width = float(params.width) / values.size();
366 for (const int i : values.index_range()) {
367 std::stringstream ss;
368 const float value = values[i];
369 ss << " " << std::fixed << std::setprecision(3) << value;
370 const std::string value_str = ss.str();
371 uiBut *but = uiDefIconTextBut(params.block,
373 0,
374 ICON_NONE,
375 value_str,
376 params.xmin + i * segment_width,
377 params.ymin,
378 segment_width,
379 params.height,
380 nullptr,
381 0,
382 0,
383 std::nullopt);
384 /* Right-align Floats. */
387
388 /* Tooltip showing raw byte values. Encode values in pointer to avoid memory allocation. */
390 but,
391 [](bContext * /*C*/, void *argN, const StringRef /*tip*/) {
392 const uint32_t uint_color = POINTER_AS_UINT(argN);
393 ColorGeometry4b color = *(ColorGeometry4b *)&uint_color;
394 return fmt::format(fmt::runtime(TIP_("Byte Color (sRGB encoded):\n{} {} {} {}")),
395 color.r,
396 color.g,
397 color.b,
398 color.a);
399 },
400 POINTER_FROM_UINT(*(uint32_t *)&color),
401 nullptr);
402 }
403 }
404
405 void draw_float4x4(const CellDrawParams &params, const float4x4 &value) const
406 {
407 uiBut *but = uiDefIconTextBut(params.block,
409 0,
410 ICON_NONE,
411 "...",
412 params.xmin,
413 params.ymin,
414 params.width,
415 params.height,
416 nullptr,
417 0,
418 0,
419 std::nullopt);
420 /* Center alignment. */
423 but,
424 [](bContext * /*C*/, void *argN, const StringRef /*tip*/) {
425 /* Transpose to be able to print row by row. */
426 const float4x4 value = math::transpose(*static_cast<const float4x4 *>(argN));
427 std::stringstream ss;
428 ss << value[0] << ",\n";
429 ss << value[1] << ",\n";
430 ss << value[2] << ",\n";
431 ss << value[3];
432 return ss.str();
433 },
434 MEM_dupallocN<float4x4>(__func__, value),
435 MEM_freeN);
436 }
437
438 int column_width(int column_index) const final
439 {
440 return spreadsheet_layout_.columns[column_index].width;
441 }
442};
443
444template<typename T>
445static float estimate_max_column_width(const float min_width,
446 const int fontid,
447 const std::optional<int64_t> max_sample_size,
448 const VArray<T> &data,
449 FunctionRef<std::string(const T &)> to_string)
450{
451 if (const std::optional<T> value = data.get_if_single()) {
452 const std::string str = to_string(*value);
453 return std::max(min_width, BLF_width(fontid, str.c_str(), str.size()));
454 }
455 const int sample_size = max_sample_size.value_or(data.size());
456 float width = min_width;
457 for (const int i : data.index_range().take_front(sample_size)) {
458 const std::string str = to_string(data[i]);
459 const float value_width = BLF_width(fontid, str.c_str(), str.size());
460 width = std::max(width, value_width);
461 }
462 return width;
463}
464
465float ColumnValues::fit_column_values_width_px(const std::optional<int64_t> &max_sample_size) const
466{
467 const int fontid = BLF_default();
469
470 auto get_min_width = [&](const float min_width) {
471 return max_sample_size.has_value() ? min_width : 0.0f;
472 };
473
474 const eSpreadsheetColumnValueType column_type = this->type();
475 switch (column_type) {
477 return 2.0f * SPREADSHEET_WIDTH_UNIT;
478 }
480 return 2.0f * SPREADSHEET_WIDTH_UNIT;
481 }
484 get_min_width(3 * SPREADSHEET_WIDTH_UNIT),
485 fontid,
486 max_sample_size,
487 data_.typed<int8_t>(),
488 [](const int value) { return fmt::format("{}", value); });
489 }
492 get_min_width(3 * SPREADSHEET_WIDTH_UNIT),
493 fontid,
494 max_sample_size,
495 data_.typed<int>(),
496 [](const int value) { return fmt::format("{}", value); });
497 }
500 get_min_width(3 * SPREADSHEET_WIDTH_UNIT),
501 fontid,
502 max_sample_size,
503 data_.typed<float>(),
504 [](const float value) { return fmt::format("{:.3f}", value); });
505 }
508 get_min_width(3 * SPREADSHEET_WIDTH_UNIT),
509 fontid,
510 max_sample_size,
511 data_.typed<int2>(),
512 [](const int2 value) { return fmt::format("{} {}", value.x, value.y); });
513 }
516 get_min_width(6 * SPREADSHEET_WIDTH_UNIT),
517 fontid,
518 max_sample_size,
519 data_.typed<float2>(),
520 [](const float2 value) { return fmt::format("{:.3f} {:.3f}", value.x, value.y); });
521 }
524 get_min_width(9 * SPREADSHEET_WIDTH_UNIT),
525 fontid,
526 max_sample_size,
527 data_.typed<float3>(),
528 [](const float3 value) {
529 return fmt::format("{:.3f} {:.3f} {:.3f}", value.x, value.y, value.z);
530 });
531 }
534 get_min_width(12 * SPREADSHEET_WIDTH_UNIT),
535 fontid,
536 max_sample_size,
537 data_.typed<ColorGeometry4f>(),
538 [](const ColorGeometry4f value) {
539 return fmt::format(
540 "{:.3f} {:.3f} {:.3f} {:.3f}", value.r, value.g, value.b, value.a);
541 });
542 }
545 get_min_width(12 * SPREADSHEET_WIDTH_UNIT),
546 fontid,
547 max_sample_size,
548 data_.typed<ColorGeometry4b>(),
549 [](const ColorGeometry4b value) {
550 return fmt::format("{} {} {} {}", value.r, value.g, value.b, value.a);
551 });
552 }
555 get_min_width(12 * SPREADSHEET_WIDTH_UNIT),
556 fontid,
557 max_sample_size,
558 data_.typed<math::Quaternion>(),
559 [](const math::Quaternion value) {
560 return fmt::format(
561 "{:.3f} {:.3f} {:.3f} {:.3f}", value.x, value.y, value.z, value.w);
562 });
563 }
565 return UI_ICON_SIZE + 0.5f * UI_UNIT_X +
567 get_min_width(8 * SPREADSHEET_WIDTH_UNIT),
568 fontid,
569 max_sample_size,
571 [](const bke::InstanceReference &value) {
572 const StringRef name = value.name().is_empty() ? IFACE_("(Geometry)") :
573 value.name();
574 return name;
575 });
576 }
578 if (data_.type().is<std::string>()) {
580 fontid,
581 max_sample_size,
582 data_.typed<std::string>(),
583 [](const StringRef value) { return value; });
584 }
585 if (data_.type().is<MStringProperty>()) {
587 get_min_width(SPREADSHEET_WIDTH_UNIT),
588 fontid,
589 max_sample_size,
590 data_.typed<MStringProperty>(),
591 [](const MStringProperty &value) { return StringRef(value.s, value.s_len); });
592 }
593 break;
594 }
596 break;
597 }
598 }
599 return 2.0f * SPREADSHEET_WIDTH_UNIT;
600}
601
602float ColumnValues::fit_column_width_px(const std::optional<int64_t> &max_sample_size) const
603{
604 const float padding_px = 0.5 * SPREADSHEET_WIDTH_UNIT;
605 const float min_width_px = SPREADSHEET_WIDTH_UNIT;
606
607 const float data_width_px = this->fit_column_values_width_px(max_sample_size);
608
609 const int fontid = BLF_default();
611 const float name_width_px = BLF_width(fontid, name_.data(), name_.size());
612
613 const float width_px = std::max(min_width_px,
614 padding_px + std::max(data_width_px, name_width_px));
615 return width_px;
616}
617
618std::unique_ptr<SpreadsheetDrawer> spreadsheet_drawer_from_layout(
619 const SpreadsheetLayout &spreadsheet_layout)
620{
621 return std::make_unique<SpreadsheetLayoutDrawer>(spreadsheet_layout);
622}
623
624} // namespace blender::ed::spreadsheet
void BLF_size(int fontid, float size)
Definition blf.cc:440
int BLF_default()
float BLF_width(int fontid, const char *str, size_t str_len, ResultBLF *r_info=nullptr) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(2)
Definition blf.cc:805
#define BLI_assert(a)
Definition BLI_assert.h:46
#define POINTER_AS_UINT(i)
#define POINTER_FROM_UINT(i)
#define TIP_(msgid)
#define IFACE_(msgid)
eSpreadsheetColumnValueType
@ SPREADSHEET_VALUE_TYPE_INT8
@ SPREADSHEET_VALUE_TYPE_FLOAT
@ SPREADSHEET_VALUE_TYPE_INT32_2D
@ SPREADSHEET_VALUE_TYPE_BYTE_COLOR
@ SPREADSHEET_VALUE_TYPE_UNKNOWN
@ SPREADSHEET_VALUE_TYPE_FLOAT3
@ SPREADSHEET_VALUE_TYPE_BOOL
@ SPREADSHEET_VALUE_TYPE_STRING
@ SPREADSHEET_VALUE_TYPE_QUATERNION
@ SPREADSHEET_VALUE_TYPE_FLOAT4X4
@ SPREADSHEET_VALUE_TYPE_INT32
@ SPREADSHEET_VALUE_TYPE_FLOAT2
@ SPREADSHEET_VALUE_TYPE_COLOR
@ SPREADSHEET_VALUE_TYPE_INSTANCES
#define SPREADSHEET_WIDTH_UNIT
#define UI_SCALE_FAC
#define UI_ICON_SIZE
uiBut * uiDefIconTextBut(uiBlock *block, int type, int retval, int icon, blender::StringRef str, int x, int y, short width, short height, void *poin, float min, float max, std::optional< blender::StringRef > tip)
void UI_but_func_tooltip_set(uiBut *but, uiButToolTipFunc func, void *arg, uiFreeArgFunc free_arg)
void UI_but_drawflag_enable(uiBut *but, int flag)
void UI_but_drawflag_disable(uiBut *but, int flag)
@ UI_BUT_TEXT_RIGHT
@ UI_BUT_ICON_LEFT
@ UI_BUT_TEXT_LEFT
#define UI_DEFAULT_TEXT_POINTS
#define UI_UNIT_X
@ UI_BTYPE_LABEL
BMesh const char void * data
ChannelStorageType r
Definition BLI_color.hh:93
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr bool is_empty() const
Definition BLI_span.hh:260
constexpr bool is_empty() const
StringRefNull name() const
Definition instances.cc:119
eSpreadsheetColumnValueType type() const
float fit_column_values_width_px(const std::optional< int64_t > &max_sample_size=std::nullopt) const
float fit_column_width_px(const std::optional< int64_t > &max_sample_size=std::nullopt) const
void draw_top_row_cell(int column_index, const CellDrawParams &params) const final
void draw_byte_color(const CellDrawParams &params, const ColorGeometry4b color) const
void draw_left_column_cell(int row_index, const CellDrawParams &params) const final
void draw_int_vector(const CellDrawParams &params, const Span< int > values) const
void draw_content_cell(int row_index, int column_index, const CellDrawParams &params) const final
void draw_float_vector(const CellDrawParams &params, const Span< float > values) const
void draw_float4x4(const CellDrawParams &params, const float4x4 &value) const
SpreadsheetLayoutDrawer(const SpreadsheetLayout &spreadsheet_layout)
#define str(s)
static const char * to_string(const Interpolation &interp)
Definition gl_shader.cc:109
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
#define T
static float estimate_max_column_width(const float min_width, const int fontid, const std::optional< int64_t > max_sample_size, const VArray< T > &data, FunctionRef< std::string(const T &)> to_string)
int get_instance_reference_icon(const bke::InstanceReference &reference)
std::unique_ptr< SpreadsheetDrawer > spreadsheet_drawer_from_layout(const SpreadsheetLayout &spreadsheet_layout)
QuaternionBase< float > Quaternion
MatBase< T, NumCol, NumRow > transpose(const MatBase< T, NumRow, NumCol > &mat)
MatBase< float, 4, 4 > float4x4
VecBase< float, 4 > float4
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
ColorSceneLinear4f< eAlpha::Premultiplied > ColorGeometry4f
Definition BLI_color.hh:342
VecBase< float, 3 > float3
blender::VecBase< int16_t, 2 > short2
ColorSceneLinearByteEncoded4b< eAlpha::Premultiplied > ColorGeometry4b
Definition BLI_color.hh:343
i
Definition text_draw.cc:230