2023-08-16 00:20:26 +10:00
|
|
|
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
2023-05-31 16:19:06 +02:00
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
2021-03-15 12:19:48 +01:00
|
|
|
|
|
|
|
|
#include <iomanip>
|
|
|
|
|
#include <sstream>
|
|
|
|
|
|
2023-08-13 10:50:52 +03:00
|
|
|
#include "BLI_math_color.hh"
|
2023-06-12 10:47:06 -04:00
|
|
|
#include "BLI_math_quaternion_types.hh"
|
2023-01-04 00:14:55 +01:00
|
|
|
#include "BLI_math_vector_types.hh"
|
2023-09-01 21:37:11 +02:00
|
|
|
#include "BLI_string.h"
|
2021-12-15 09:34:13 -06:00
|
|
|
|
|
|
|
|
#include "BKE_geometry_set.hh"
|
2022-10-17 11:39:40 +02:00
|
|
|
#include "BKE_instances.hh"
|
2021-12-15 09:34:13 -06:00
|
|
|
|
|
|
|
|
#include "spreadsheet_column_values.hh"
|
2021-04-09 10:20:46 +02:00
|
|
|
#include "spreadsheet_layout.hh"
|
2021-03-15 12:19:48 +01:00
|
|
|
|
2021-12-15 09:34:13 -06:00
|
|
|
#include "DNA_collection_types.h"
|
|
|
|
|
#include "DNA_object_types.h"
|
2021-03-15 12:19:48 +01:00
|
|
|
#include "DNA_userdef_types.h"
|
|
|
|
|
|
2023-08-05 02:57:52 +02:00
|
|
|
#include "UI_interface.hh"
|
|
|
|
|
#include "UI_resources.hh"
|
2021-03-15 12:19:48 +01:00
|
|
|
|
|
|
|
|
#include "BLF_api.h"
|
|
|
|
|
|
2022-06-29 13:01:38 +02:00
|
|
|
#include "BLT_translation.h"
|
|
|
|
|
|
2021-03-15 12:19:48 +01:00
|
|
|
namespace blender::ed::spreadsheet {
|
|
|
|
|
|
2021-04-09 10:20:46 +02:00
|
|
|
class SpreadsheetLayoutDrawer : public SpreadsheetDrawer {
|
2021-03-15 12:19:48 +01:00
|
|
|
private:
|
2021-04-09 10:20:46 +02:00
|
|
|
const SpreadsheetLayout &spreadsheet_layout_;
|
2021-03-15 12:19:48 +01:00
|
|
|
|
|
|
|
|
public:
|
2021-04-09 10:20:46 +02:00
|
|
|
SpreadsheetLayoutDrawer(const SpreadsheetLayout &spreadsheet_layout)
|
|
|
|
|
: spreadsheet_layout_(spreadsheet_layout)
|
2021-03-15 12:19:48 +01:00
|
|
|
{
|
2021-04-09 10:20:46 +02:00
|
|
|
tot_columns = spreadsheet_layout.columns.size();
|
|
|
|
|
tot_rows = spreadsheet_layout.row_indices.size();
|
|
|
|
|
left_column_width = spreadsheet_layout.index_column_width;
|
2021-03-15 12:19:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void draw_top_row_cell(int column_index, const CellDrawParams ¶ms) const final
|
|
|
|
|
{
|
2021-04-09 10:20:46 +02:00
|
|
|
const StringRefNull name = spreadsheet_layout_.columns[column_index].values->name();
|
2021-03-15 12:19:48 +01:00
|
|
|
uiBut *but = uiDefIconTextBut(params.block,
|
|
|
|
|
UI_BTYPE_LABEL,
|
|
|
|
|
0,
|
|
|
|
|
ICON_NONE,
|
|
|
|
|
name.c_str(),
|
|
|
|
|
params.xmin,
|
|
|
|
|
params.ymin,
|
|
|
|
|
params.width,
|
|
|
|
|
params.height,
|
|
|
|
|
nullptr,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
nullptr);
|
|
|
|
|
/* 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
|
|
|
|
|
{
|
2021-04-09 10:20:46 +02:00
|
|
|
const int real_index = spreadsheet_layout_.row_indices[row_index];
|
2021-03-15 12:19:48 +01:00
|
|
|
std::string index_str = std::to_string(real_index);
|
|
|
|
|
uiBut *but = uiDefIconTextBut(params.block,
|
|
|
|
|
UI_BTYPE_LABEL,
|
|
|
|
|
0,
|
|
|
|
|
ICON_NONE,
|
|
|
|
|
index_str.c_str(),
|
|
|
|
|
params.xmin,
|
|
|
|
|
params.ymin,
|
|
|
|
|
params.width,
|
|
|
|
|
params.height,
|
|
|
|
|
nullptr,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
nullptr);
|
|
|
|
|
/* 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
|
|
|
|
|
{
|
2021-04-09 10:20:46 +02:00
|
|
|
const int real_index = spreadsheet_layout_.row_indices[row_index];
|
|
|
|
|
const ColumnValues &column = *spreadsheet_layout_.columns[column_index].values;
|
2021-12-15 09:34:13 -06:00
|
|
|
if (real_index > column.size()) {
|
|
|
|
|
return;
|
2021-10-26 11:25:32 +02:00
|
|
|
}
|
2021-03-15 12:19:48 +01:00
|
|
|
|
2022-03-19 08:26:29 +01:00
|
|
|
const GVArray &data = column.data();
|
2021-12-15 09:34:13 -06:00
|
|
|
|
|
|
|
|
if (data.type().is<int>()) {
|
|
|
|
|
const int value = data.get<int>(real_index);
|
2021-03-15 12:19:48 +01:00
|
|
|
const std::string value_str = std::to_string(value);
|
2022-02-04 10:29:11 -06:00
|
|
|
uiBut *but = uiDefIconTextBut(params.block,
|
|
|
|
|
UI_BTYPE_LABEL,
|
|
|
|
|
0,
|
|
|
|
|
ICON_NONE,
|
|
|
|
|
value_str.c_str(),
|
|
|
|
|
params.xmin,
|
|
|
|
|
params.ymin,
|
|
|
|
|
params.width,
|
|
|
|
|
params.height,
|
|
|
|
|
nullptr,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
nullptr);
|
|
|
|
|
/* 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);
|
2021-04-02 09:03:36 -07:00
|
|
|
uiBut *but = uiDefIconTextBut(params.block,
|
|
|
|
|
UI_BTYPE_LABEL,
|
|
|
|
|
0,
|
|
|
|
|
ICON_NONE,
|
|
|
|
|
value_str.c_str(),
|
|
|
|
|
params.xmin,
|
|
|
|
|
params.ymin,
|
|
|
|
|
params.width,
|
|
|
|
|
params.height,
|
|
|
|
|
nullptr,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
nullptr);
|
|
|
|
|
/* Right-align Integers. */
|
|
|
|
|
UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
|
|
|
|
|
UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT);
|
2021-03-15 12:19:48 +01:00
|
|
|
}
|
2023-04-14 16:08:05 +02:00
|
|
|
else if (data.type().is<int2>()) {
|
|
|
|
|
const int2 value = data.get<int2>(real_index);
|
|
|
|
|
this->draw_int_vector(params, Span(&value.x, 2));
|
|
|
|
|
}
|
2021-12-15 09:34:13 -06:00
|
|
|
else if (data.type().is<float>()) {
|
|
|
|
|
const float value = data.get<float>(real_index);
|
2021-03-15 12:19:48 +01:00
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << std::fixed << std::setprecision(3) << value;
|
|
|
|
|
const std::string value_str = ss.str();
|
2021-04-02 09:03:36 -07:00
|
|
|
uiBut *but = uiDefIconTextBut(params.block,
|
|
|
|
|
UI_BTYPE_LABEL,
|
|
|
|
|
0,
|
|
|
|
|
ICON_NONE,
|
|
|
|
|
value_str.c_str(),
|
|
|
|
|
params.xmin,
|
|
|
|
|
params.ymin,
|
|
|
|
|
params.width,
|
|
|
|
|
params.height,
|
|
|
|
|
nullptr,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
nullptr);
|
|
|
|
|
/* Right-align Floats. */
|
|
|
|
|
UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
|
|
|
|
|
UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT);
|
2021-03-15 12:19:48 +01:00
|
|
|
}
|
2021-12-15 09:34:13 -06:00
|
|
|
else if (data.type().is<bool>()) {
|
|
|
|
|
const bool value = data.get<bool>(real_index);
|
2021-03-15 12:19:48 +01:00
|
|
|
const int icon = value ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT;
|
2021-04-02 14:26:16 -07:00
|
|
|
uiBut *but = uiDefIconTextBut(params.block,
|
|
|
|
|
UI_BTYPE_LABEL,
|
|
|
|
|
0,
|
|
|
|
|
icon,
|
|
|
|
|
"",
|
|
|
|
|
params.xmin,
|
|
|
|
|
params.ymin,
|
|
|
|
|
params.width,
|
|
|
|
|
params.height,
|
|
|
|
|
nullptr,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
nullptr);
|
|
|
|
|
UI_but_drawflag_disable(but, UI_BUT_ICON_LEFT);
|
2021-03-15 12:19:48 +01:00
|
|
|
}
|
2021-12-15 09:34:13 -06:00
|
|
|
else if (data.type().is<float2>()) {
|
|
|
|
|
const float2 value = data.get<float2>(real_index);
|
2021-04-26 09:09:50 +02:00
|
|
|
this->draw_float_vector(params, Span(&value.x, 2));
|
|
|
|
|
}
|
2021-12-15 09:34:13 -06:00
|
|
|
else if (data.type().is<float3>()) {
|
|
|
|
|
const float3 value = data.get<float3>(real_index);
|
2021-04-26 09:09:50 +02:00
|
|
|
this->draw_float_vector(params, Span(&value.x, 3));
|
|
|
|
|
}
|
2021-12-15 09:34:13 -06:00
|
|
|
else if (data.type().is<ColorGeometry4f>()) {
|
|
|
|
|
const ColorGeometry4f value = data.get<ColorGeometry4f>(real_index);
|
2021-04-26 09:09:50 +02:00
|
|
|
this->draw_float_vector(params, Span(&value.r, 4));
|
|
|
|
|
}
|
2022-04-21 16:11:26 +02:00
|
|
|
else if (data.type().is<ColorGeometry4b>()) {
|
|
|
|
|
const ColorGeometry4b value = data.get<ColorGeometry4b>(real_index);
|
2022-06-29 13:01:38 +02:00
|
|
|
this->draw_byte_color(params, value);
|
2022-04-21 16:11:26 +02:00
|
|
|
}
|
2023-06-12 10:47:06 -04:00
|
|
|
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));
|
|
|
|
|
}
|
2022-10-17 11:39:40 +02:00
|
|
|
else if (data.type().is<bke::InstanceReference>()) {
|
|
|
|
|
const bke::InstanceReference value = data.get<bke::InstanceReference>(real_index);
|
2021-12-15 09:34:13 -06:00
|
|
|
switch (value.type()) {
|
2022-10-17 11:39:40 +02:00
|
|
|
case bke::InstanceReference::Type::Object: {
|
2021-12-15 09:34:13 -06:00
|
|
|
const Object &object = value.object();
|
|
|
|
|
uiDefIconTextBut(params.block,
|
|
|
|
|
UI_BTYPE_LABEL,
|
|
|
|
|
0,
|
|
|
|
|
ICON_OBJECT_DATA,
|
|
|
|
|
object.id.name + 2,
|
|
|
|
|
params.xmin,
|
|
|
|
|
params.ymin,
|
|
|
|
|
params.width,
|
|
|
|
|
params.height,
|
|
|
|
|
nullptr,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
nullptr);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2022-10-17 11:39:40 +02:00
|
|
|
case bke::InstanceReference::Type::Collection: {
|
2021-12-15 09:34:13 -06:00
|
|
|
Collection &collection = value.collection();
|
|
|
|
|
uiDefIconTextBut(params.block,
|
|
|
|
|
UI_BTYPE_LABEL,
|
|
|
|
|
0,
|
|
|
|
|
ICON_OUTLINER_COLLECTION,
|
|
|
|
|
collection.id.name + 2,
|
|
|
|
|
params.xmin,
|
|
|
|
|
params.ymin,
|
|
|
|
|
params.width,
|
|
|
|
|
params.height,
|
|
|
|
|
nullptr,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
nullptr);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2022-10-17 11:39:40 +02:00
|
|
|
case bke::InstanceReference::Type::GeometrySet: {
|
2021-12-15 09:34:13 -06:00
|
|
|
uiDefIconTextBut(params.block,
|
|
|
|
|
UI_BTYPE_LABEL,
|
|
|
|
|
0,
|
|
|
|
|
ICON_MESH_DATA,
|
|
|
|
|
"Geometry",
|
|
|
|
|
params.xmin,
|
|
|
|
|
params.ymin,
|
|
|
|
|
params.width,
|
|
|
|
|
params.height,
|
|
|
|
|
nullptr,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
nullptr);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2022-10-17 11:39:40 +02:00
|
|
|
case bke::InstanceReference::Type::None: {
|
2021-12-15 09:34:13 -06:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-11-03 13:45:51 -05:00
|
|
|
}
|
2022-02-14 18:00:45 -06:00
|
|
|
else if (data.type().is<std::string>()) {
|
|
|
|
|
uiDefIconTextBut(params.block,
|
|
|
|
|
UI_BTYPE_LABEL,
|
|
|
|
|
0,
|
|
|
|
|
ICON_NONE,
|
|
|
|
|
data.get<std::string>(real_index).c_str(),
|
|
|
|
|
params.xmin,
|
|
|
|
|
params.ymin,
|
|
|
|
|
params.width,
|
|
|
|
|
params.height,
|
|
|
|
|
nullptr,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
nullptr);
|
|
|
|
|
}
|
2021-03-15 12:19:48 +01:00
|
|
|
}
|
|
|
|
|
|
2021-04-26 09:09:50 +02:00
|
|
|
void draw_float_vector(const CellDrawParams ¶ms, const Span<float> values) const
|
|
|
|
|
{
|
|
|
|
|
BLI_assert(!values.is_empty());
|
2022-09-25 18:33:28 +10:00
|
|
|
const float segment_width = float(params.width) / values.size();
|
2021-04-26 09:09:50 +02:00
|
|
|
for (const int i : values.index_range()) {
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
const float value = values[i];
|
2022-07-26 12:35:55 +02:00
|
|
|
ss << " " << std::fixed << std::setprecision(3) << value;
|
2021-04-26 09:09:50 +02:00
|
|
|
const std::string value_str = ss.str();
|
2023-04-14 16:08:05 +02:00
|
|
|
uiBut *but = uiDefIconTextBut(params.block,
|
|
|
|
|
UI_BTYPE_LABEL,
|
|
|
|
|
0,
|
|
|
|
|
ICON_NONE,
|
|
|
|
|
value_str.c_str(),
|
|
|
|
|
params.xmin + i * segment_width,
|
|
|
|
|
params.ymin,
|
|
|
|
|
segment_width,
|
|
|
|
|
params.height,
|
|
|
|
|
nullptr,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
nullptr);
|
|
|
|
|
/* 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();
|
2021-04-26 09:09:50 +02:00
|
|
|
uiBut *but = uiDefIconTextBut(params.block,
|
|
|
|
|
UI_BTYPE_LABEL,
|
|
|
|
|
0,
|
|
|
|
|
ICON_NONE,
|
|
|
|
|
value_str.c_str(),
|
|
|
|
|
params.xmin + i * segment_width,
|
|
|
|
|
params.ymin,
|
|
|
|
|
segment_width,
|
|
|
|
|
params.height,
|
|
|
|
|
nullptr,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
nullptr);
|
|
|
|
|
/* Right-align Floats. */
|
|
|
|
|
UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
|
|
|
|
|
UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-29 13:01:38 +02:00
|
|
|
void draw_byte_color(const CellDrawParams ¶ms, const ColorGeometry4b color) const
|
2022-04-21 16:11:26 +02:00
|
|
|
{
|
2022-06-29 13:01:38 +02:00
|
|
|
const ColorGeometry4f float_color = color.decode();
|
|
|
|
|
Span<float> values(&float_color.r, 4);
|
2022-09-25 18:33:28 +10:00
|
|
|
const float segment_width = float(params.width) / values.size();
|
2022-04-21 16:11:26 +02:00
|
|
|
for (const int i : values.index_range()) {
|
2022-06-29 13:01:38 +02:00
|
|
|
std::stringstream ss;
|
|
|
|
|
const float value = values[i];
|
2022-07-26 12:35:55 +02:00
|
|
|
ss << " " << std::fixed << std::setprecision(3) << value;
|
2022-06-29 13:01:38 +02:00
|
|
|
const std::string value_str = ss.str();
|
2022-04-21 16:11:26 +02:00
|
|
|
uiBut *but = uiDefIconTextBut(params.block,
|
|
|
|
|
UI_BTYPE_LABEL,
|
|
|
|
|
0,
|
|
|
|
|
ICON_NONE,
|
|
|
|
|
value_str.c_str(),
|
|
|
|
|
params.xmin + i * segment_width,
|
|
|
|
|
params.ymin,
|
|
|
|
|
segment_width,
|
|
|
|
|
params.height,
|
|
|
|
|
nullptr,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
nullptr);
|
2022-06-29 13:01:38 +02:00
|
|
|
/* Right-align Floats. */
|
2022-04-21 16:11:26 +02:00
|
|
|
UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
|
|
|
|
|
UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT);
|
2022-06-29 13:01:38 +02:00
|
|
|
|
|
|
|
|
/* Tooltip showing raw byte values. Encode values in pointer to avoid memory allocation. */
|
|
|
|
|
UI_but_func_tooltip_set(
|
|
|
|
|
but,
|
2022-10-03 17:37:25 -05:00
|
|
|
[](bContext * /*C*/, void *argN, const char * /*tip*/) {
|
2022-06-29 13:01:38 +02:00
|
|
|
const uint32_t uint_color = POINTER_AS_UINT(argN);
|
|
|
|
|
ColorGeometry4b color = *(ColorGeometry4b *)&uint_color;
|
|
|
|
|
return BLI_sprintfN(TIP_("Byte Color (sRGB encoded):\n%3d %3d %3d %3d"),
|
|
|
|
|
color.r,
|
|
|
|
|
color.g,
|
|
|
|
|
color.b,
|
|
|
|
|
color.a);
|
|
|
|
|
},
|
|
|
|
|
POINTER_FROM_UINT(*(uint32_t *)&color),
|
|
|
|
|
nullptr);
|
2022-04-21 16:11:26 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-15 12:19:48 +01:00
|
|
|
int column_width(int column_index) const final
|
|
|
|
|
{
|
2021-04-09 10:20:46 +02:00
|
|
|
return spreadsheet_layout_.columns[column_index].width;
|
2021-03-15 12:19:48 +01:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2021-04-09 10:20:46 +02:00
|
|
|
std::unique_ptr<SpreadsheetDrawer> spreadsheet_drawer_from_layout(
|
|
|
|
|
const SpreadsheetLayout &spreadsheet_layout)
|
2021-03-15 12:19:48 +01:00
|
|
|
{
|
2021-04-09 10:20:46 +02:00
|
|
|
return std::make_unique<SpreadsheetLayoutDrawer>(spreadsheet_layout);
|
2021-03-15 12:19:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace blender::ed::spreadsheet
|