The general idea is to keep the 'old', C-style MEM_callocN signature, and slowly replace most of its usages with the new, C++-style type-safer template version. * `MEM_cnew<T>` allocation version is renamed to `MEM_callocN<T>`. * `MEM_cnew_array<T>` allocation version is renamed to `MEM_calloc_arrayN<T>`. * `MEM_cnew<T>` duplicate version is renamed to `MEM_dupallocN<T>`. Similar templates type-safe version of `MEM_mallocN` will be added soon as well. Following discussions in !134452. NOTE: For now static type checking in `MEM_callocN` and related are slightly different for Windows MSVC. This compiler seems to consider structs using the `DNA_DEFINE_CXX_METHODS` macro as non-trivial (likely because their default copy constructors are deleted). So using checks on trivially constructible/destructible instead on this compiler/system. Pull Request: https://projects.blender.org/blender/blender/pulls/134771
442 lines
17 KiB
C++
442 lines
17 KiB
C++
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
#include <iomanip>
|
|
#include <sstream>
|
|
|
|
#include <fmt/format.h>
|
|
|
|
#include "BLI_math_matrix.hh"
|
|
#include "BLI_math_quaternion_types.hh"
|
|
#include "BLI_math_vector_types.hh"
|
|
|
|
#include "BKE_instances.hh"
|
|
|
|
#include "spreadsheet_column_values.hh"
|
|
#include "spreadsheet_data_source_geometry.hh"
|
|
#include "spreadsheet_layout.hh"
|
|
|
|
#include "DNA_meshdata_types.h"
|
|
|
|
#include "UI_interface.hh"
|
|
#include "UI_resources.hh"
|
|
|
|
#include "BLT_translation.hh"
|
|
|
|
namespace blender::ed::spreadsheet {
|
|
|
|
class SpreadsheetLayoutDrawer : public SpreadsheetDrawer {
|
|
private:
|
|
const SpreadsheetLayout &spreadsheet_layout_;
|
|
|
|
public:
|
|
SpreadsheetLayoutDrawer(const SpreadsheetLayout &spreadsheet_layout)
|
|
: spreadsheet_layout_(spreadsheet_layout)
|
|
{
|
|
tot_columns = spreadsheet_layout.columns.size();
|
|
tot_rows = spreadsheet_layout.row_indices.size();
|
|
left_column_width = spreadsheet_layout.index_column_width;
|
|
}
|
|
|
|
void draw_top_row_cell(int column_index, const CellDrawParams ¶ms) const final
|
|
{
|
|
const StringRefNull name = spreadsheet_layout_.columns[column_index].values->name();
|
|
uiBut *but = uiDefIconTextBut(params.block,
|
|
UI_BTYPE_LABEL,
|
|
0,
|
|
ICON_NONE,
|
|
name,
|
|
params.xmin,
|
|
params.ymin,
|
|
params.width,
|
|
params.height,
|
|
nullptr,
|
|
0,
|
|
0,
|
|
std::nullopt);
|
|
/* Center-align column headers. */
|
|
UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
|
|
UI_but_drawflag_disable(but, UI_BUT_TEXT_RIGHT);
|
|
}
|
|
|
|
void draw_left_column_cell(int row_index, const CellDrawParams ¶ms) const final
|
|
{
|
|
const int real_index = spreadsheet_layout_.row_indices[row_index];
|
|
std::string index_str = std::to_string(real_index);
|
|
uiBut *but = uiDefIconTextBut(params.block,
|
|
UI_BTYPE_LABEL,
|
|
0,
|
|
ICON_NONE,
|
|
index_str,
|
|
params.xmin,
|
|
params.ymin,
|
|
params.width,
|
|
params.height,
|
|
nullptr,
|
|
0,
|
|
0,
|
|
std::nullopt);
|
|
/* Right-align indices. */
|
|
UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT);
|
|
UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
|
|
}
|
|
|
|
void draw_content_cell(int row_index, int column_index, const CellDrawParams ¶ms) const final
|
|
{
|
|
const int real_index = spreadsheet_layout_.row_indices[row_index];
|
|
const ColumnValues &column = *spreadsheet_layout_.columns[column_index].values;
|
|
if (real_index > column.size()) {
|
|
return;
|
|
}
|
|
|
|
const GVArray &data = column.data();
|
|
|
|
if (data.type().is<int>()) {
|
|
const int value = data.get<int>(real_index);
|
|
const std::string value_str = std::to_string(value);
|
|
uiBut *but = uiDefIconTextBut(params.block,
|
|
UI_BTYPE_LABEL,
|
|
0,
|
|
ICON_NONE,
|
|
value_str,
|
|
params.xmin,
|
|
params.ymin,
|
|
params.width,
|
|
params.height,
|
|
nullptr,
|
|
0,
|
|
0,
|
|
std::nullopt);
|
|
UI_but_func_tooltip_set(
|
|
but,
|
|
[](bContext * /*C*/, void *argN, const StringRef /*tip*/) {
|
|
return fmt::format("{}", *((int *)argN));
|
|
},
|
|
MEM_dupallocN<int>(__func__, value),
|
|
MEM_freeN);
|
|
/* Right-align Integers. */
|
|
UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
|
|
UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT);
|
|
}
|
|
if (data.type().is<int8_t>()) {
|
|
const int8_t value = data.get<int8_t>(real_index);
|
|
const std::string value_str = std::to_string(value);
|
|
uiBut *but = uiDefIconTextBut(params.block,
|
|
UI_BTYPE_LABEL,
|
|
0,
|
|
ICON_NONE,
|
|
value_str,
|
|
params.xmin,
|
|
params.ymin,
|
|
params.width,
|
|
params.height,
|
|
nullptr,
|
|
0,
|
|
0,
|
|
std::nullopt);
|
|
/* Right-align Integers. */
|
|
UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
|
|
UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT);
|
|
}
|
|
else if (data.type().is<short2>()) {
|
|
const int2 value = int2(data.get<short2>(real_index));
|
|
this->draw_int_vector(params, Span(&value.x, 2));
|
|
}
|
|
else if (data.type().is<int2>()) {
|
|
const int2 value = data.get<int2>(real_index);
|
|
this->draw_int_vector(params, Span(&value.x, 2));
|
|
}
|
|
else if (data.type().is<float>()) {
|
|
const float value = data.get<float>(real_index);
|
|
std::stringstream ss;
|
|
ss << std::fixed << std::setprecision(3) << value;
|
|
const std::string value_str = ss.str();
|
|
uiBut *but = uiDefIconTextBut(params.block,
|
|
UI_BTYPE_LABEL,
|
|
0,
|
|
ICON_NONE,
|
|
value_str,
|
|
params.xmin,
|
|
params.ymin,
|
|
params.width,
|
|
params.height,
|
|
nullptr,
|
|
0,
|
|
0,
|
|
std::nullopt);
|
|
UI_but_func_tooltip_set(
|
|
but,
|
|
[](bContext * /*C*/, void *argN, const StringRef /*tip*/) {
|
|
return fmt::format("{:f}", *((float *)argN));
|
|
},
|
|
MEM_dupallocN<float>(__func__, value),
|
|
MEM_freeN);
|
|
/* Right-align Floats. */
|
|
UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
|
|
UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT);
|
|
}
|
|
else if (data.type().is<bool>()) {
|
|
const bool value = data.get<bool>(real_index);
|
|
const int icon = value ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT;
|
|
uiBut *but = uiDefIconTextBut(params.block,
|
|
UI_BTYPE_LABEL,
|
|
0,
|
|
icon,
|
|
"",
|
|
params.xmin,
|
|
params.ymin,
|
|
params.width,
|
|
params.height,
|
|
nullptr,
|
|
0,
|
|
0,
|
|
std::nullopt);
|
|
UI_but_drawflag_disable(but, UI_BUT_ICON_LEFT);
|
|
}
|
|
else if (data.type().is<float2>()) {
|
|
const float2 value = data.get<float2>(real_index);
|
|
this->draw_float_vector(params, Span(&value.x, 2));
|
|
}
|
|
else if (data.type().is<float3>()) {
|
|
const float3 value = data.get<float3>(real_index);
|
|
this->draw_float_vector(params, Span(&value.x, 3));
|
|
}
|
|
else if (data.type().is<ColorGeometry4f>()) {
|
|
const ColorGeometry4f value = data.get<ColorGeometry4f>(real_index);
|
|
this->draw_float_vector(params, Span(&value.r, 4));
|
|
}
|
|
else if (data.type().is<ColorGeometry4b>()) {
|
|
const ColorGeometry4b value = data.get<ColorGeometry4b>(real_index);
|
|
this->draw_byte_color(params, value);
|
|
}
|
|
else if (data.type().is<math::Quaternion>()) {
|
|
const float4 value = float4(data.get<math::Quaternion>(real_index));
|
|
this->draw_float_vector(params, Span(&value.x, 4));
|
|
}
|
|
else if (data.type().is<float4x4>()) {
|
|
this->draw_float4x4(params, data.get<float4x4>(real_index));
|
|
}
|
|
else if (data.type().is<bke::InstanceReference>()) {
|
|
const bke::InstanceReference value = data.get<bke::InstanceReference>(real_index);
|
|
const StringRefNull name = value.name().is_empty() ? IFACE_("(Geometry)") : value.name();
|
|
const int icon = get_instance_reference_icon(value);
|
|
uiDefIconTextBut(params.block,
|
|
UI_BTYPE_LABEL,
|
|
0,
|
|
icon,
|
|
name.c_str(),
|
|
params.xmin,
|
|
params.ymin,
|
|
params.width,
|
|
params.height,
|
|
nullptr,
|
|
0,
|
|
0,
|
|
std::nullopt);
|
|
}
|
|
else if (data.type().is<std::string>()) {
|
|
uiDefIconTextBut(params.block,
|
|
UI_BTYPE_LABEL,
|
|
0,
|
|
ICON_NONE,
|
|
data.get<std::string>(real_index),
|
|
params.xmin,
|
|
params.ymin,
|
|
params.width,
|
|
params.height,
|
|
nullptr,
|
|
0,
|
|
0,
|
|
std::nullopt);
|
|
}
|
|
else if (data.type().is<MStringProperty>()) {
|
|
MStringProperty *prop = MEM_callocN<MStringProperty>(__func__);
|
|
data.get_to_uninitialized(real_index, prop);
|
|
uiBut *but = uiDefIconTextBut(params.block,
|
|
UI_BTYPE_LABEL,
|
|
0,
|
|
ICON_NONE,
|
|
StringRef(prop->s, prop->s_len),
|
|
params.xmin,
|
|
params.ymin,
|
|
params.width,
|
|
params.height,
|
|
nullptr,
|
|
0,
|
|
0,
|
|
std::nullopt);
|
|
|
|
UI_but_func_tooltip_set(
|
|
but,
|
|
[](bContext * /*C*/, void *argN, const StringRef /*tip*/) {
|
|
const MStringProperty &prop = *static_cast<MStringProperty *>(argN);
|
|
return std::string(StringRef(prop.s, prop.s_len));
|
|
},
|
|
prop,
|
|
MEM_freeN);
|
|
}
|
|
}
|
|
|
|
void draw_float_vector(const CellDrawParams ¶ms, const Span<float> values) const
|
|
{
|
|
BLI_assert(!values.is_empty());
|
|
const float segment_width = float(params.width) / values.size();
|
|
for (const int i : values.index_range()) {
|
|
std::stringstream ss;
|
|
const float value = values[i];
|
|
ss << " " << std::fixed << std::setprecision(3) << value;
|
|
const std::string value_str = ss.str();
|
|
uiBut *but = uiDefIconTextBut(params.block,
|
|
UI_BTYPE_LABEL,
|
|
0,
|
|
ICON_NONE,
|
|
value_str,
|
|
params.xmin + i * segment_width,
|
|
params.ymin,
|
|
segment_width,
|
|
params.height,
|
|
nullptr,
|
|
0,
|
|
0,
|
|
std::nullopt);
|
|
|
|
UI_but_func_tooltip_set(
|
|
but,
|
|
[](bContext * /*C*/, void *argN, const StringRef /*tip*/) {
|
|
return fmt::format("{:f}", *((float *)argN));
|
|
},
|
|
MEM_dupallocN<float>(__func__, value),
|
|
MEM_freeN);
|
|
/* Right-align Floats. */
|
|
UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
|
|
UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT);
|
|
}
|
|
}
|
|
|
|
void draw_int_vector(const CellDrawParams ¶ms, const Span<int> values) const
|
|
{
|
|
BLI_assert(!values.is_empty());
|
|
const float segment_width = float(params.width) / values.size();
|
|
for (const int i : values.index_range()) {
|
|
std::stringstream ss;
|
|
const int value = values[i];
|
|
ss << " " << value;
|
|
const std::string value_str = ss.str();
|
|
uiBut *but = uiDefIconTextBut(params.block,
|
|
UI_BTYPE_LABEL,
|
|
0,
|
|
ICON_NONE,
|
|
value_str,
|
|
params.xmin + i * segment_width,
|
|
params.ymin,
|
|
segment_width,
|
|
params.height,
|
|
nullptr,
|
|
0,
|
|
0,
|
|
std::nullopt);
|
|
UI_but_func_tooltip_set(
|
|
but,
|
|
[](bContext * /*C*/, void *argN, const StringRef /*tip*/) {
|
|
return fmt::format("{}", *((int *)argN));
|
|
},
|
|
MEM_dupallocN<int>(__func__, value),
|
|
MEM_freeN);
|
|
/* Right-align Floats. */
|
|
UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
|
|
UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT);
|
|
}
|
|
}
|
|
|
|
void draw_byte_color(const CellDrawParams ¶ms, const ColorGeometry4b color) const
|
|
{
|
|
const ColorGeometry4f float_color = color.decode();
|
|
Span<float> values(&float_color.r, 4);
|
|
const float segment_width = float(params.width) / values.size();
|
|
for (const int i : values.index_range()) {
|
|
std::stringstream ss;
|
|
const float value = values[i];
|
|
ss << " " << std::fixed << std::setprecision(3) << value;
|
|
const std::string value_str = ss.str();
|
|
uiBut *but = uiDefIconTextBut(params.block,
|
|
UI_BTYPE_LABEL,
|
|
0,
|
|
ICON_NONE,
|
|
value_str,
|
|
params.xmin + i * segment_width,
|
|
params.ymin,
|
|
segment_width,
|
|
params.height,
|
|
nullptr,
|
|
0,
|
|
0,
|
|
std::nullopt);
|
|
/* Right-align Floats. */
|
|
UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
|
|
UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT);
|
|
|
|
/* Tooltip showing raw byte values. Encode values in pointer to avoid memory allocation. */
|
|
UI_but_func_tooltip_set(
|
|
but,
|
|
[](bContext * /*C*/, void *argN, const StringRef /*tip*/) {
|
|
const uint32_t uint_color = POINTER_AS_UINT(argN);
|
|
ColorGeometry4b color = *(ColorGeometry4b *)&uint_color;
|
|
return fmt::format(fmt::runtime(TIP_("Byte Color (sRGB encoded):\n{} {} {} {}")),
|
|
color.r,
|
|
color.g,
|
|
color.b,
|
|
color.a);
|
|
},
|
|
POINTER_FROM_UINT(*(uint32_t *)&color),
|
|
nullptr);
|
|
}
|
|
}
|
|
|
|
void draw_float4x4(const CellDrawParams ¶ms, const float4x4 &value) const
|
|
{
|
|
uiBut *but = uiDefIconTextBut(params.block,
|
|
UI_BTYPE_LABEL,
|
|
0,
|
|
ICON_NONE,
|
|
"...",
|
|
params.xmin,
|
|
params.ymin,
|
|
params.width,
|
|
params.height,
|
|
nullptr,
|
|
0,
|
|
0,
|
|
std::nullopt);
|
|
/* Center alignment. */
|
|
UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
|
|
UI_but_func_tooltip_set(
|
|
but,
|
|
[](bContext * /*C*/, void *argN, const StringRef /*tip*/) {
|
|
/* Transpose to be able to print row by row. */
|
|
const float4x4 value = math::transpose(*static_cast<const float4x4 *>(argN));
|
|
std::stringstream ss;
|
|
ss << value[0] << ",\n";
|
|
ss << value[1] << ",\n";
|
|
ss << value[2] << ",\n";
|
|
ss << value[3];
|
|
return ss.str();
|
|
},
|
|
MEM_dupallocN<float4x4>(__func__, value),
|
|
MEM_freeN);
|
|
}
|
|
|
|
int column_width(int column_index) const final
|
|
{
|
|
return spreadsheet_layout_.columns[column_index].width;
|
|
}
|
|
};
|
|
|
|
std::unique_ptr<SpreadsheetDrawer> spreadsheet_drawer_from_layout(
|
|
const SpreadsheetLayout &spreadsheet_layout)
|
|
{
|
|
return std::make_unique<SpreadsheetLayoutDrawer>(spreadsheet_layout);
|
|
}
|
|
|
|
} // namespace blender::ed::spreadsheet
|