Spreadsheet: persistent table layouts

The main goal of this patch is that the column widths and ordering is not reset
every time one switches between different contexts.

It does that by keeping track of multiple `SpreadsheetTable`. There is one for
each table that is viewed (so e.g. the point and edge domain of the same mesh
are two different tables). Each table has an identifier and an array of columns.

There is some garbage collection in place so that the number of stored tables
does not increase unbounded.

This also comes with an updated Python API:
```python
import bpy
spreadsheet = bpy.context.screen.areas[...].spaces.active
active_table = spreadsheet.tables.active
print(active_table.id.type)
print(active_table.id.attribute_domain)
print(active_table.columns[0].id.name)
```

In the future, we might add some smarter logic to keep tables with different
identifiers more in sync. We don't have a great heuristic for that yet.

Pull Request: https://projects.blender.org/blender/blender/pulls/139205
This commit is contained in:
Jacques Lucke
2025-05-23 06:07:04 +02:00
parent 0265b13399
commit 0b24f15939
22 changed files with 1014 additions and 228 deletions

View File

@@ -47,6 +47,7 @@ void BKE_viewer_path_copy(ViewerPath *dst, const ViewerPath *src);
bool BKE_viewer_path_equal(const ViewerPath *a,
const ViewerPath *b,
ViewerPathEqualFlag flag = ViewerPathEqualFlag(0));
uint64_t BKE_viewer_path_hash(const ViewerPath &viewer_path);
void BKE_viewer_path_blend_write(BlendWriter *writer, const ViewerPath *viewer_path);
void BKE_viewer_path_blend_read_data(BlendDataReader *reader, ViewerPath *viewer_path);
void BKE_viewer_path_foreach_id(LibraryForeachIDData *data, ViewerPath *viewer_path);
@@ -67,4 +68,5 @@ ViewerPathElem *BKE_viewer_path_elem_copy(const ViewerPathElem *src);
bool BKE_viewer_path_elem_equal(const ViewerPathElem *a,
const ViewerPathElem *b,
ViewerPathEqualFlag flag = ViewerPathEqualFlag(0));
uint64_t BKE_viewer_path_elem_hash(const ViewerPathElem &elem);
void BKE_viewer_path_elem_free(ViewerPathElem *elem);

View File

@@ -60,6 +60,16 @@ bool BKE_viewer_path_equal(const ViewerPath *a,
return false;
}
uint64_t BKE_viewer_path_hash(const ViewerPath &viewer_path)
{
uint64_t hash = 0;
LISTBASE_FOREACH (ViewerPathElem *, elem, &viewer_path.path) {
const uint64_t elem_hash = BKE_viewer_path_elem_hash(*elem);
hash = blender::get_default_hash(hash, elem_hash);
}
return hash;
}
void BKE_viewer_path_blend_write(BlendWriter *writer, const ViewerPath *viewer_path)
{
LISTBASE_FOREACH (ViewerPathElem *, elem, &viewer_path->path) {
@@ -393,6 +403,53 @@ bool BKE_viewer_path_elem_equal(const ViewerPathElem *a,
return false;
}
uint64_t BKE_viewer_path_elem_hash(const ViewerPathElem &elem)
{
using blender::get_default_hash;
switch (ViewerPathElemType(elem.type)) {
case VIEWER_PATH_ELEM_TYPE_ID: {
const auto &typed_elem = reinterpret_cast<const IDViewerPathElem &>(elem);
return get_default_hash(elem.type, typed_elem.id ? typed_elem.id->session_uid : 0);
}
case VIEWER_PATH_ELEM_TYPE_MODIFIER: {
const auto &typed_elem = reinterpret_cast<const ModifierViewerPathElem &>(elem);
return get_default_hash(elem.type, typed_elem.modifier_uid);
}
case VIEWER_PATH_ELEM_TYPE_GROUP_NODE: {
const auto &typed_elem = reinterpret_cast<const GroupNodeViewerPathElem &>(elem);
return get_default_hash(elem.type, typed_elem.node_id);
}
case VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE: {
const auto &typed_elem = reinterpret_cast<const SimulationZoneViewerPathElem &>(elem);
return get_default_hash(elem.type, typed_elem.sim_output_node_id);
}
case VIEWER_PATH_ELEM_TYPE_VIEWER_NODE: {
const auto &typed_elem = reinterpret_cast<const ViewerNodeViewerPathElem &>(elem);
return get_default_hash(elem.type, typed_elem.node_id);
}
case VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE: {
const auto &typed_elem = reinterpret_cast<const RepeatZoneViewerPathElem &>(elem);
return get_default_hash(elem.type, typed_elem.repeat_output_node_id, typed_elem.iteration);
}
case VIEWER_PATH_ELEM_TYPE_FOREACH_GEOMETRY_ELEMENT_ZONE: {
const auto &typed_elem = reinterpret_cast<const ForeachGeometryElementZoneViewerPathElem &>(
elem);
return get_default_hash(elem.type, typed_elem.zone_output_node_id, typed_elem.index);
}
case VIEWER_PATH_ELEM_TYPE_EVALUATE_CLOSURE: {
const auto &typed_elem = reinterpret_cast<const EvaluateClosureNodeViewerPathElem &>(elem);
return get_default_hash(
elem.type,
typed_elem.evaluate_node_id,
typed_elem.source_output_node_id,
typed_elem.source_node_tree ?
reinterpret_cast<const ID *>(typed_elem.source_node_tree)->session_uid :
0);
}
}
return 0;
}
void BKE_viewer_path_elem_free(ViewerPathElem *elem)
{
switch (ViewerPathElemType(elem->type)) {

View File

@@ -1942,20 +1942,6 @@ void blo_do_versions_290(FileData *fd, Library * /*lib*/, Main *bmain)
}
}
}
/* Consolidate node and final evaluation modes. */
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype == SPACE_SPREADSHEET) {
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
if (sspreadsheet->object_eval_state == 2) {
sspreadsheet->object_eval_state = SPREADSHEET_OBJECT_EVAL_STATE_EVALUATED;
}
}
}
}
}
}
/* Set default value for the new bisect_threshold parameter in the mirror modifier. */

View File

@@ -6,9 +6,13 @@
struct ID;
struct SpaceSpreadsheet;
struct SpreadsheetTable;
namespace blender::ed::spreadsheet {
ID *get_current_id(const SpaceSpreadsheet *sspreadsheet);
}
SpreadsheetTable *get_active_table(SpaceSpreadsheet &sspreadsheet);
const SpreadsheetTable *get_active_table(const SpaceSpreadsheet &sspreadsheet);
} // namespace blender::ed::spreadsheet

View File

@@ -25,6 +25,7 @@ set(SRC
spreadsheet_panels.cc
spreadsheet_row_filter.cc
spreadsheet_row_filter_ui.cc
spreadsheet_table.cc
spreadsheet_column.hh
spreadsheet_column_values.hh
@@ -36,6 +37,7 @@ set(SRC
spreadsheet_layout.hh
spreadsheet_row_filter.hh
spreadsheet_row_filter_ui.hh
spreadsheet_table.hh
)
set(LIB

View File

@@ -43,6 +43,7 @@
#include "spreadsheet_layout.hh"
#include "spreadsheet_row_filter.hh"
#include "spreadsheet_row_filter_ui.hh"
#include "spreadsheet_table.hh"
#include <sstream>
@@ -53,6 +54,7 @@ static SpaceLink *spreadsheet_create(const ScrArea * /*area*/, const Scene * /*s
SpaceSpreadsheet *spreadsheet_space = MEM_callocN<SpaceSpreadsheet>("spreadsheet space");
spreadsheet_space->spacetype = SPACE_SPREADSHEET;
spreadsheet_space->geometry_id.base.type = SPREADSHEET_TABLE_ID_TYPE_GEOMETRY;
spreadsheet_space->filter_flag = SPREADSHEET_FILTER_ENABLE;
{
@@ -107,11 +109,11 @@ static void spreadsheet_free(SpaceLink *sl)
LISTBASE_FOREACH_MUTABLE (SpreadsheetRowFilter *, row_filter, &sspreadsheet->row_filters) {
spreadsheet_row_filter_free(row_filter);
}
LISTBASE_FOREACH_MUTABLE (SpreadsheetColumn *, column, &sspreadsheet->columns) {
spreadsheet_column_free(column);
for (const int i : IndexRange(sspreadsheet->num_tables)) {
spreadsheet_table_free(sspreadsheet->tables[i]);
}
MEM_SAFE_FREE(sspreadsheet->instance_ids);
BKE_viewer_path_clear(&sspreadsheet->viewer_path);
MEM_SAFE_FREE(sspreadsheet->tables);
spreadsheet_table_id_free_content(&sspreadsheet->geometry_id.base);
}
static void spreadsheet_init(wmWindowManager * /*wm*/, ScrArea *area)
@@ -139,16 +141,15 @@ static SpaceLink *spreadsheet_duplicate(SpaceLink *sl)
SpreadsheetRowFilter *new_filter = spreadsheet_row_filter_copy(src_filter);
BLI_addtail(&sspreadsheet_new->row_filters, new_filter);
}
BLI_listbase_clear(&sspreadsheet_new->columns);
LISTBASE_FOREACH (SpreadsheetColumn *, src_column, &sspreadsheet_old->columns) {
SpreadsheetColumn *new_column = spreadsheet_column_copy(src_column);
BLI_addtail(&sspreadsheet_new->columns, new_column);
sspreadsheet_new->num_tables = sspreadsheet_old->num_tables;
sspreadsheet_new->tables = MEM_calloc_arrayN<SpreadsheetTable *>(sspreadsheet_old->num_tables,
__func__);
for (const int i : IndexRange(sspreadsheet_old->num_tables)) {
sspreadsheet_new->tables[i] = spreadsheet_table_copy(*sspreadsheet_old->tables[i]);
}
sspreadsheet_new->instance_ids = static_cast<SpreadsheetInstanceID *>(
MEM_dupallocN(sspreadsheet_old->instance_ids));
BKE_viewer_path_copy(&sspreadsheet_new->viewer_path, &sspreadsheet_old->viewer_path);
spreadsheet_table_id_copy_content_geometry(sspreadsheet_new->geometry_id,
sspreadsheet_old->geometry_id);
return (SpaceLink *)sspreadsheet_new;
}
@@ -163,13 +164,19 @@ static void spreadsheet_id_remap(ScrArea * /*area*/,
const blender::bke::id::IDRemapper &mappings)
{
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)slink;
BKE_viewer_path_id_remap(&sspreadsheet->viewer_path, mappings);
spreadsheet_table_id_remap_id(sspreadsheet->geometry_id.base, mappings);
for (const int i : IndexRange(sspreadsheet->num_tables)) {
spreadsheet_table_remap_id(*sspreadsheet->tables[i], mappings);
}
}
static void spreadsheet_foreach_id(SpaceLink *space_link, LibraryForeachIDData *data)
{
SpaceSpreadsheet *sspreadsheet = reinterpret_cast<SpaceSpreadsheet *>(space_link);
BKE_viewer_path_foreach_id(data, &sspreadsheet->viewer_path);
spreadsheet_table_id_foreach_id(sspreadsheet->geometry_id.base, data);
for (const int i : IndexRange(sspreadsheet->num_tables)) {
spreadsheet_table_foreach_id(*sspreadsheet->tables[i], data);
}
}
static void spreadsheet_main_region_init(wmWindowManager *wm, ARegion *region)
@@ -197,11 +204,11 @@ static void spreadsheet_main_region_init(wmWindowManager *wm, ARegion *region)
ID *get_current_id(const SpaceSpreadsheet *sspreadsheet)
{
if (BLI_listbase_is_empty(&sspreadsheet->viewer_path.path)) {
if (BLI_listbase_is_empty(&sspreadsheet->geometry_id.viewer_path.path)) {
return nullptr;
}
ViewerPathElem *root_context = static_cast<ViewerPathElem *>(
sspreadsheet->viewer_path.path.first);
sspreadsheet->geometry_id.viewer_path.path.first);
if (root_context->type != VIEWER_PATH_ELEM_TYPE_ID) {
return nullptr;
}
@@ -211,14 +218,14 @@ ID *get_current_id(const SpaceSpreadsheet *sspreadsheet)
static void view_active_object(const bContext *C, SpaceSpreadsheet *sspreadsheet)
{
BKE_viewer_path_clear(&sspreadsheet->viewer_path);
BKE_viewer_path_clear(&sspreadsheet->geometry_id.viewer_path);
Object *ob = CTX_data_active_object(C);
if (ob == nullptr) {
return;
}
IDViewerPathElem *id_elem = BKE_viewer_path_elem_new_id();
id_elem->id = &ob->id;
BLI_addtail(&sspreadsheet->viewer_path.path, id_elem);
BLI_addtail(&sspreadsheet->geometry_id.viewer_path.path, id_elem);
ED_area_tag_redraw(CTX_wm_area(C));
}
@@ -228,8 +235,9 @@ static void spreadsheet_update_context(const bContext *C)
SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
Object *active_object = CTX_data_active_object(C);
Object *context_object = blender::ed::viewer_path::parse_object_only(sspreadsheet->viewer_path);
switch (eSpaceSpreadsheet_ObjectEvalState(sspreadsheet->object_eval_state)) {
Object *context_object = blender::ed::viewer_path::parse_object_only(
sspreadsheet->geometry_id.viewer_path);
switch (eSpaceSpreadsheet_ObjectEvalState(sspreadsheet->geometry_id.object_eval_state)) {
case SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL:
case SPREADSHEET_OBJECT_EVAL_STATE_EVALUATED: {
if (sspreadsheet->flag & SPREADSHEET_FLAG_PINNED) {
@@ -258,7 +266,8 @@ static void spreadsheet_update_context(const bContext *C)
WorkSpace *workspace = CTX_wm_workspace(C);
if (sspreadsheet->flag & SPREADSHEET_FLAG_PINNED) {
const std::optional<ViewerPathForGeometryNodesViewer> parsed_path =
blender::ed::viewer_path::parse_geometry_nodes_viewer(sspreadsheet->viewer_path);
blender::ed::viewer_path::parse_geometry_nodes_viewer(
sspreadsheet->geometry_id.viewer_path);
if (parsed_path.has_value()) {
if (blender::ed::viewer_path::exists_geometry_nodes_viewer(*parsed_path)) {
/* The pinned path is still valid, do nothing. */
@@ -276,7 +285,7 @@ static void spreadsheet_update_context(const bContext *C)
const std::optional<ViewerPathForGeometryNodesViewer> workspace_parsed_path =
blender::ed::viewer_path::parse_geometry_nodes_viewer(workspace->viewer_path);
if (workspace_parsed_path.has_value()) {
if (BKE_viewer_path_equal(&sspreadsheet->viewer_path,
if (BKE_viewer_path_equal(&sspreadsheet->geometry_id.viewer_path,
&workspace->viewer_path,
VIEWER_PATH_EQUAL_FLAG_CONSIDER_UI_NAME))
{
@@ -284,12 +293,12 @@ static void spreadsheet_update_context(const bContext *C)
break;
}
/* Update the viewer path from the workspace. */
BKE_viewer_path_clear(&sspreadsheet->viewer_path);
BKE_viewer_path_copy(&sspreadsheet->viewer_path, &workspace->viewer_path);
BKE_viewer_path_clear(&sspreadsheet->geometry_id.viewer_path);
BKE_viewer_path_copy(&sspreadsheet->geometry_id.viewer_path, &workspace->viewer_path);
}
else {
/* No active viewer node, change back to showing evaluated active object. */
sspreadsheet->object_eval_state = SPREADSHEET_OBJECT_EVAL_STATE_EVALUATED;
sspreadsheet->geometry_id.object_eval_state = SPREADSHEET_OBJECT_EVAL_STATE_EVALUATED;
view_active_object(C, sspreadsheet);
}
@@ -342,6 +351,26 @@ std::unique_ptr<DataSource> get_data_source(const bContext &C)
return {};
}
const SpreadsheetTableID *get_active_table_id(const SpaceSpreadsheet &sspreadsheet)
{
return &sspreadsheet.geometry_id.base;
}
SpreadsheetTable *get_active_table(SpaceSpreadsheet &sspreadsheet)
{
return const_cast<SpreadsheetTable *>(
get_active_table(const_cast<const SpaceSpreadsheet &>(sspreadsheet)));
}
const SpreadsheetTable *get_active_table(const SpaceSpreadsheet &sspreadsheet)
{
const SpreadsheetTableID *active_table_id = get_active_table_id(sspreadsheet);
if (!active_table_id) {
return nullptr;
}
return spreadsheet_table_find(sspreadsheet, *active_table_id);
}
static int get_index_column_width(const int tot_rows)
{
const int fontid = BLF_default();
@@ -350,42 +379,51 @@ static int get_index_column_width(const int tot_rows)
UI_UNIT_X * 0.75;
}
static void update_visible_columns(ListBase &columns, DataSource &data_source)
static void update_visible_columns(SpreadsheetTable &table, DataSource &data_source)
{
Set<SpreadsheetColumnID> used_ids;
LISTBASE_FOREACH_MUTABLE (SpreadsheetColumn *, column, &columns) {
std::unique_ptr<ColumnValues> values = data_source.get_column_values(*column->id);
/* Remove columns that don't exist anymore. */
if (!values) {
BLI_remlink(&columns, column);
spreadsheet_column_free(column);
continue;
}
if (!used_ids.add(*column->id)) {
/* Remove duplicate columns for now. */
BLI_remlink(&columns, column);
spreadsheet_column_free(column);
continue;
Set<std::reference_wrapper<const SpreadsheetColumnID>> handled_columns;
Vector<SpreadsheetColumn *, 32> new_columns;
for (SpreadsheetColumn *column : Span{table.columns, table.num_columns}) {
const bool still_exists = data_source.get_column_values(*column->id) != nullptr;
if (still_exists) {
if (handled_columns.add(*column->id)) {
new_columns.append(column);
continue;
}
}
/* Free columns that don't exist anymore or are duplicates for some reason. */
spreadsheet_column_free(column);
}
data_source.foreach_default_column_ids(
[&](const SpreadsheetColumnID &column_id, const bool is_extra) {
std::unique_ptr<ColumnValues> values = data_source.get_column_values(column_id);
if (values) {
if (used_ids.add(column_id)) {
SpreadsheetColumnID *new_id = spreadsheet_column_id_copy(&column_id);
SpreadsheetColumn *new_column = spreadsheet_column_new(new_id);
if (is_extra) {
BLI_addhead(&columns, new_column);
}
else {
BLI_addtail(&columns, new_column);
}
}
if (handled_columns.contains(column_id)) {
return;
}
std::unique_ptr<ColumnValues> values = data_source.get_column_values(column_id);
if (!values) {
return;
}
SpreadsheetColumn *column = spreadsheet_column_new(spreadsheet_column_id_copy(&column_id));
if (is_extra) {
new_columns.insert(0, column);
}
else {
new_columns.append(column);
}
handled_columns.add(*column->id);
});
if (Span(table.columns, table.num_columns) == new_columns.as_span()) {
/* Nothing changed. */
return;
}
/* Update the stored column pointers. */
MEM_SAFE_FREE(table.columns);
table.columns = MEM_calloc_arrayN<SpreadsheetColumn *>(new_columns.size(), __func__);
table.num_columns = new_columns.size();
std::copy_n(new_columns.begin(), new_columns.size(), table.columns);
}
static void spreadsheet_main_region_draw(const bContext *C, ARegion *region)
@@ -398,7 +436,27 @@ static void spreadsheet_main_region_draw(const bContext *C, ARegion *region)
data_source = std::make_unique<DataSource>();
}
update_visible_columns(sspreadsheet->columns, *data_source);
const SpreadsheetTableID *active_table_id = get_active_table_id(*sspreadsheet);
SpreadsheetTable *table = spreadsheet_table_find(*sspreadsheet, *active_table_id);
if (!table) {
spreadsheet_table_remove_unused(*sspreadsheet);
table = spreadsheet_table_new(spreadsheet_table_id_copy(*active_table_id));
spreadsheet_table_add(*sspreadsheet, table);
}
/* Update the last used time on the table. */
if (table->last_used < sspreadsheet->table_use_clock || sspreadsheet->table_use_clock == 0) {
sspreadsheet->table_use_clock++;
/* Handle clock overflow by just resetting all clocks. */
if (sspreadsheet->table_use_clock == 0) {
for (SpreadsheetTable *table : Span(sspreadsheet->tables, sspreadsheet->num_tables)) {
table->last_used = sspreadsheet->table_use_clock;
}
}
table->last_used = sspreadsheet->table_use_clock;
}
update_visible_columns(*table, *data_source);
SpreadsheetLayout spreadsheet_layout;
ResourceScope scope;
@@ -408,7 +466,7 @@ static void spreadsheet_main_region_draw(const bContext *C, ARegion *region)
int x = spreadsheet_layout.index_column_width;
LISTBASE_FOREACH (SpreadsheetColumn *, column, &sspreadsheet->columns) {
for (SpreadsheetColumn *column : Span{table->columns, table->num_columns}) {
std::unique_ptr<ColumnValues> values_ptr = data_source->get_column_values(*column->id);
/* Should have been removed before if it does not exist anymore. */
BLI_assert(values_ptr);
@@ -485,7 +543,8 @@ static void spreadsheet_main_region_listener(const wmRegionListenerParams *param
break;
}
case NC_VIEWER_PATH: {
if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE) {
if (sspreadsheet->geometry_id.object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE)
{
ED_region_tag_redraw(region);
}
break;
@@ -542,7 +601,8 @@ static void spreadsheet_header_region_listener(const wmRegionListenerParams *par
break;
}
case NC_VIEWER_PATH: {
if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE) {
if (sspreadsheet->geometry_id.object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE)
{
ED_region_tag_redraw(region);
}
break;
@@ -648,21 +708,15 @@ static void spreadsheet_blend_read_data(BlendDataReader *reader, SpaceLink *sl)
LISTBASE_FOREACH (SpreadsheetRowFilter *, row_filter, &sspreadsheet->row_filters) {
BLO_read_string(reader, &row_filter->value_string);
}
BLO_read_struct_list(reader, SpreadsheetColumn, &sspreadsheet->columns);
LISTBASE_FOREACH (SpreadsheetColumn *, column, &sspreadsheet->columns) {
column->runtime = MEM_new<SpreadsheetColumnRuntime>(__func__);
BLO_read_struct(reader, SpreadsheetColumnID, &column->id);
BLO_read_string(reader, &column->id->name);
/* While the display name is technically runtime data, it is loaded here, otherwise the row
* filters might not now their type if their region draws before the main region.
* This would ideally be cleared here. */
BLO_read_string(reader, &column->display_name);
BLO_read_pointer_array(
reader, sspreadsheet->num_tables, reinterpret_cast<void **>(&sspreadsheet->tables));
for (const int i : IndexRange(sspreadsheet->num_tables)) {
BLO_read_struct(reader, SpreadsheetTable, &sspreadsheet->tables[i]);
spreadsheet_table_blend_read(reader, sspreadsheet->tables[i]);
}
BLO_read_struct_array(
reader, SpreadsheetInstanceID, sspreadsheet->instance_ids_num, &sspreadsheet->instance_ids);
BKE_viewer_path_blend_read_data(reader, &sspreadsheet->viewer_path);
spreadsheet_table_id_blend_read(reader, &sspreadsheet->geometry_id.base);
}
static void spreadsheet_blend_write(BlendWriter *writer, SpaceLink *sl)
@@ -675,19 +729,12 @@ static void spreadsheet_blend_write(BlendWriter *writer, SpaceLink *sl)
BLO_write_string(writer, row_filter->value_string);
}
LISTBASE_FOREACH (SpreadsheetColumn *, column, &sspreadsheet->columns) {
BLO_write_struct(writer, SpreadsheetColumn, column);
BLO_write_struct(writer, SpreadsheetColumnID, column->id);
BLO_write_string(writer, column->id->name);
/* While the display name is technically runtime data, we write it here, otherwise the row
* filters might not now their type if their region draws before the main region.
* This would ideally be cleared here. */
BLO_write_string(writer, column->display_name);
BLO_write_pointer_array(writer, sspreadsheet->num_tables, sspreadsheet->tables);
for (const int i : IndexRange(sspreadsheet->num_tables)) {
spreadsheet_table_blend_write(writer, sspreadsheet->tables[i]);
}
BLO_write_struct_array(
writer, SpreadsheetInstanceID, sspreadsheet->instance_ids_num, sspreadsheet->instance_ids);
BKE_viewer_path_blend_write(writer, &sspreadsheet->viewer_path);
spreadsheet_table_id_blend_write_content_geometry(writer, &sspreadsheet->geometry_id);
}
static void spreadsheet_cursor(wmWindow *win, ScrArea *area, ARegion *region)

View File

@@ -5,6 +5,8 @@
#include "DNA_meshdata_types.h"
#include "DNA_space_types.h"
#include "BLO_read_write.hh"
#include "MEM_guardedalloc.h"
#include "BLI_color.hh"
@@ -88,6 +90,17 @@ void spreadsheet_column_id_free(SpreadsheetColumnID *column_id)
MEM_freeN(column_id);
}
void spreadsheet_column_id_blend_write(BlendWriter *writer, const SpreadsheetColumnID *column_id)
{
BLO_write_struct(writer, SpreadsheetColumnID, column_id);
BLO_write_string(writer, column_id->name);
}
void spreadsheet_column_id_blend_read(BlendDataReader *reader, SpreadsheetColumnID *column_id)
{
BLO_read_string(reader, &column_id->name);
}
SpreadsheetColumn *spreadsheet_column_new(SpreadsheetColumnID *column_id)
{
SpreadsheetColumn *column = MEM_callocN<SpreadsheetColumn>(__func__);
@@ -124,4 +137,19 @@ void spreadsheet_column_free(SpreadsheetColumn *column)
MEM_freeN(column);
}
void spreadsheet_column_blend_write(BlendWriter *writer, const SpreadsheetColumn *column)
{
BLO_write_struct(writer, SpreadsheetColumn, column);
spreadsheet_column_id_blend_write(writer, column->id);
BLO_write_string(writer, column->display_name);
}
void spreadsheet_column_blend_read(BlendDataReader *reader, SpreadsheetColumn *column)
{
column->runtime = MEM_new<SpreadsheetColumnRuntime>(__func__);
BLO_read_struct(reader, SpreadsheetColumnID, &column->id);
spreadsheet_column_id_blend_read(reader, column->id);
BLO_read_string(reader, &column->display_name);
}
} // namespace blender::ed::spreadsheet

View File

@@ -34,6 +34,8 @@ struct SpreadsheetColumnRuntime {
SpreadsheetColumnID *spreadsheet_column_id_new();
SpreadsheetColumnID *spreadsheet_column_id_copy(const SpreadsheetColumnID *src_column_id);
void spreadsheet_column_id_free(SpreadsheetColumnID *column_id);
void spreadsheet_column_id_blend_write(BlendWriter *writer, const SpreadsheetColumnID *column_id);
void spreadsheet_column_id_blend_read(BlendDataReader *reader, SpreadsheetColumnID *column_id);
SpreadsheetColumn *spreadsheet_column_new(SpreadsheetColumnID *column_id);
SpreadsheetColumn *spreadsheet_column_copy(const SpreadsheetColumn *src_column);
@@ -41,5 +43,7 @@ void spreadsheet_column_assign_runtime_data(SpreadsheetColumn *column,
eSpreadsheetColumnValueType data_type,
const StringRefNull display_name);
void spreadsheet_column_free(SpreadsheetColumn *column);
void spreadsheet_column_blend_write(BlendWriter *writer, const SpreadsheetColumn *column);
void spreadsheet_column_blend_read(BlendDataReader *reader, SpreadsheetColumn *column);
} // namespace blender::ed::spreadsheet

View File

@@ -48,6 +48,21 @@
using blender::nodes::geo_eval_log::ViewerNodeLog;
uint64_t SpreadsheetInstanceID::hash() const
{
return blender::get_default_hash(this->reference_index);
}
bool operator==(const SpreadsheetInstanceID &a, const SpreadsheetInstanceID &b)
{
return a.reference_index == b.reference_index;
}
bool operator!=(const SpreadsheetInstanceID &a, const SpreadsheetInstanceID &b)
{
return !(a == b);
}
namespace blender::ed::spreadsheet {
static void add_mesh_debug_column_names(
@@ -629,7 +644,7 @@ bke::GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *ss
Object *object_eval)
{
bke::GeometrySet geometry_set;
if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL) {
if (sspreadsheet->geometry_id.object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL) {
const Object *object_orig = DEG_get_original(object_eval);
if (object_orig->type == OB_MESH) {
const Mesh *mesh = static_cast<const Mesh *>(object_orig->data);
@@ -664,13 +679,13 @@ bke::GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *ss
}
}
else {
if (BLI_listbase_is_single(&sspreadsheet->viewer_path.path)) {
if (BLI_listbase_is_single(&sspreadsheet->geometry_id.viewer_path.path)) {
geometry_set = bke::object_get_evaluated_geometry_set(*object_eval);
}
else {
if (const ViewerNodeLog *viewer_log =
nodes::geo_eval_log::GeoModifierLog::find_viewer_node_log_for_path(
sspreadsheet->viewer_path))
sspreadsheet->geometry_id.viewer_path))
{
geometry_set = viewer_log->geometry;
}
@@ -709,11 +724,13 @@ std::unique_ptr<DataSource> data_source_from_geometry(const bContext *C, Object
const bke::GeometrySet root_geometry_set = spreadsheet_get_display_geometry_set(sspreadsheet,
object_eval);
const bke::GeometrySet geometry_set = get_geometry_set_for_instance_ids(
root_geometry_set, Span{sspreadsheet->instance_ids, sspreadsheet->instance_ids_num});
root_geometry_set,
Span{sspreadsheet->geometry_id.instance_ids, sspreadsheet->geometry_id.instance_ids_num});
const bke::AttrDomain domain = (bke::AttrDomain)sspreadsheet->attribute_domain;
const auto component_type = bke::GeometryComponent::Type(sspreadsheet->geometry_component_type);
const int active_layer_index = sspreadsheet->active_layer_index;
const bke::AttrDomain domain = (bke::AttrDomain)sspreadsheet->geometry_id.attribute_domain;
const auto component_type = bke::GeometryComponent::Type(
sspreadsheet->geometry_id.geometry_component_type);
const int layer_index = sspreadsheet->geometry_id.layer_index;
if (!geometry_set.has(component_type)) {
return {};
}
@@ -721,10 +738,11 @@ std::unique_ptr<DataSource> data_source_from_geometry(const bContext *C, Object
if (component_type == bke::GeometryComponent::Type::Volume) {
return std::make_unique<VolumeDataSource>(std::move(geometry_set));
}
Object *object_orig = sspreadsheet->instance_ids_num == 0 ? DEG_get_original(object_eval) :
nullptr;
Object *object_orig = sspreadsheet->geometry_id.instance_ids_num == 0 ?
DEG_get_original(object_eval) :
nullptr;
return std::make_unique<GeometryDataSource>(
object_orig, std::move(geometry_set), component_type, domain, active_layer_index);
object_orig, std::move(geometry_set), component_type, domain, layer_index);
}
} // namespace blender::ed::spreadsheet

View File

@@ -664,11 +664,11 @@ std::optional<bool> InstancesTreeViewItem::should_be_active() const
Vector<SpreadsheetInstanceID> instance_ids;
this->get_parent_instance_ids(instance_ids);
if (sspreadsheet.instance_ids_num != instance_ids.size()) {
if (sspreadsheet.geometry_id.instance_ids_num != instance_ids.size()) {
return false;
}
for (const int i : instance_ids.index_range()) {
const SpreadsheetInstanceID &a = sspreadsheet.instance_ids[i];
const SpreadsheetInstanceID &a = sspreadsheet.geometry_id.instance_ids[i];
const SpreadsheetInstanceID &b = instance_ids[i];
if (a.reference_index != b.reference_index) {
return false;
@@ -684,11 +684,12 @@ void InstancesTreeViewItem::on_activate(bContext &C)
SpaceSpreadsheet &sspreadsheet = *CTX_wm_space_spreadsheet(&C);
MEM_SAFE_FREE(sspreadsheet.instance_ids);
sspreadsheet.instance_ids = MEM_calloc_arrayN<SpreadsheetInstanceID>(instance_ids.size(),
__func__);
sspreadsheet.instance_ids_num = instance_ids.size();
initialized_copy_n(instance_ids.data(), instance_ids.size(), sspreadsheet.instance_ids);
MEM_SAFE_FREE(sspreadsheet.geometry_id.instance_ids);
sspreadsheet.geometry_id.instance_ids = MEM_calloc_arrayN<SpreadsheetInstanceID>(
instance_ids.size(), __func__);
sspreadsheet.geometry_id.instance_ids_num = instance_ids.size();
initialized_copy_n(
instance_ids.data(), instance_ids.size(), sspreadsheet.geometry_id.instance_ids);
WM_main_add_notifier(NC_SPACE | ND_SPACE_SPREADSHEET, nullptr);
}
@@ -713,12 +714,12 @@ void DataSetViewItem::on_activate(bContext &C)
bScreen &screen = *CTX_wm_screen(&C);
SpaceSpreadsheet &sspreadsheet = *CTX_wm_space_spreadsheet(&C);
sspreadsheet.geometry_component_type = uint8_t(data_id->component_type);
sspreadsheet.geometry_id.geometry_component_type = uint8_t(data_id->component_type);
if (data_id->domain) {
sspreadsheet.attribute_domain = uint8_t(*data_id->domain);
sspreadsheet.geometry_id.attribute_domain = uint8_t(*data_id->domain);
}
if (data_id->layer_index) {
sspreadsheet.active_layer_index = *data_id->layer_index;
sspreadsheet.geometry_id.layer_index = *data_id->layer_index;
}
PointerRNA ptr = RNA_pointer_create_discrete(&screen.id, &RNA_SpaceSpreadsheet, &sspreadsheet);
/* These updates also make sure that the attribute domain is set properly based on the
@@ -736,18 +737,18 @@ std::optional<bool> DataSetViewItem::should_be_active() const
if (!data_id) {
return false;
}
if (bke::GeometryComponent::Type(sspreadsheet.geometry_component_type) !=
if (bke::GeometryComponent::Type(sspreadsheet.geometry_id.geometry_component_type) !=
data_id->component_type)
{
return false;
}
if (data_id->domain) {
if (bke::AttrDomain(sspreadsheet.attribute_domain) != data_id->domain) {
if (bke::AttrDomain(sspreadsheet.geometry_id.attribute_domain) != data_id->domain) {
return false;
}
}
if (data_id->layer_index) {
if (sspreadsheet.active_layer_index != *data_id->layer_index) {
if (sspreadsheet.geometry_id.layer_index != *data_id->layer_index) {
return false;
}
}
@@ -923,7 +924,7 @@ class ViewerPathTreeView : public ui::AbstractTreeView {
void build_tree() override
{
const ViewerPath &viewer_path = sspreadsheet_.viewer_path;
const ViewerPath &viewer_path = sspreadsheet_.geometry_id.viewer_path;
int index;
LISTBASE_FOREACH_INDEX (const ViewerPathElem *, elem, &viewer_path.path, index) {
@@ -1023,7 +1024,7 @@ static void draw_context_panel_content(const bContext &C, uiLayout &layout)
bScreen &screen = *CTX_wm_screen(&C);
SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(&C);
ViewerPath &viewer_path = sspreadsheet->viewer_path;
ViewerPath &viewer_path = sspreadsheet->geometry_id.viewer_path;
ID *root_id = get_current_id(sspreadsheet);
if (!root_id) {
draw_context_panel_without_context(layout);
@@ -1039,7 +1040,7 @@ static void draw_context_panel_content(const bContext &C, uiLayout &layout)
layout.prop(&sspreadsheet_ptr, "object_eval_state", UI_ITEM_NONE, "", ICON_NONE);
if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE &&
if (sspreadsheet->geometry_id.object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE &&
viewer_path_ends_with_viewer_node(viewer_path))
{
if (uiLayout *panel = layout.panel(&C, "viewer path", true, IFACE_("Viewer Path"))) {
@@ -1057,7 +1058,7 @@ static void draw_context_panel(const bContext &C, uiLayout &layout)
if (ID *root_id = get_current_id(&sspreadsheet)) {
std::string label = BKE_id_name(*root_id);
if (!context_panel.body) {
switch (sspreadsheet.object_eval_state) {
switch (sspreadsheet.geometry_id.object_eval_state) {
case SPREADSHEET_OBJECT_EVAL_STATE_EVALUATED:
label += " (Evaluated)";
break;
@@ -1111,7 +1112,8 @@ void spreadsheet_data_set_panel_draw(const bContext *C, Panel *panel)
}
if (uiLayout *panel = layout->panel(C, "geometry_domain_tree_view", false, IFACE_("Domain"))) {
bke::GeometrySet instance_geometry = get_geometry_set_for_instance_ids(
root_geometry, {sspreadsheet->instance_ids, sspreadsheet->instance_ids_num});
root_geometry,
{sspreadsheet->geometry_id.instance_ids, sspreadsheet->geometry_id.instance_ids_num});
ui::AbstractTreeView *tree_view = UI_block_add_view(
*block,
"Data Set Tree View",

View File

@@ -17,6 +17,8 @@
#include "BLI_rect.h"
#include "ED_spreadsheet.hh"
#include "spreadsheet_column.hh"
#include "spreadsheet_draw.hh"
#include "spreadsheet_intern.hh"
@@ -277,10 +279,12 @@ static void draw_column_reorder_source(const uint pos,
{
const ReorderColumnVisualizationData &data =
*sspreadsheet.runtime->reorder_column_visualization_data;
const SpreadsheetTable &table = *get_active_table(sspreadsheet);
const SpreadsheetColumn &moving_column = *table.columns[data.old_index];
rctf rect;
rect.xmin = data.column_to_move->runtime->left_x - scroll_offset_x;
rect.xmax = data.column_to_move->runtime->right_x - scroll_offset_x;
rect.xmin = moving_column.runtime->left_x - scroll_offset_x;
rect.xmax = moving_column.runtime->right_x - scroll_offset_x;
rect.ymin = 0;
rect.ymax = region.winy;
@@ -297,6 +301,9 @@ static void draw_column_reorder_destination(const ARegion &region,
{
const ReorderColumnVisualizationData &data =
*sspreadsheet.runtime->reorder_column_visualization_data;
const SpreadsheetTable &table = *get_active_table(sspreadsheet);
const SpreadsheetColumn &moving_column = *table.columns[data.old_index];
const SpreadsheetColumn &insert_column = *table.columns[data.new_index];
{
/* Draw column that is moved. */
@@ -304,10 +311,10 @@ static void draw_column_reorder_destination(const ARegion &region,
UI_GetThemeColorShade4fv(TH_BACK, -20, color);
color.a = 0.3f;
rctf offset_column_rect;
offset_column_rect.xmin = data.column_to_move->runtime->left_x + data.current_offset_x_px -
offset_column_rect.xmin = moving_column.runtime->left_x + data.current_offset_x_px -
scroll_offset_x;
offset_column_rect.xmax = offset_column_rect.xmin +
data.column_to_move->width * SPREADSHEET_WIDTH_UNIT;
moving_column.width * SPREADSHEET_WIDTH_UNIT;
offset_column_rect.ymin = 0;
offset_column_rect.ymax = region.winy;
UI_draw_roundbox_4fv(&offset_column_rect, true, 0, color);
@@ -317,10 +324,8 @@ static void draw_column_reorder_destination(const ARegion &region,
ColorTheme4f color;
UI_GetThemeColorShade4fv(TH_TEXT, 20, color);
color.a = 0.6f;
const SpreadsheetColumn *first_column = static_cast<const SpreadsheetColumn *>(
sspreadsheet.columns.first);
const int insert_column_x = data.new_prev_column ? data.new_prev_column->runtime->right_x :
first_column->runtime->left_x;
const int insert_column_x = data.new_index <= data.old_index ? insert_column.runtime->left_x :
insert_column.runtime->right_x;
const int width = UI_UNIT_X * 0.1f;
rctf insert_rect;
insert_rect.xmin = insert_column_x - width / 2 - scroll_offset_x;

View File

@@ -6,6 +6,8 @@
#include "BKE_geometry_set.hh"
#include "DNA_space_types.h"
struct ARegionType;
struct Depsgraph;
struct Object;
@@ -21,8 +23,8 @@ namespace blender::ed::spreadsheet {
class DataSource;
struct ReorderColumnVisualizationData {
SpreadsheetColumn *column_to_move = nullptr;
SpreadsheetColumn *new_prev_column = nullptr;
int old_index = 0;
int new_index = 0;
int current_offset_x_px = 0;
};
@@ -78,4 +80,10 @@ SpreadsheetColumn *find_hovered_column(SpaceSpreadsheet &sspreadsheet,
*/
std::unique_ptr<DataSource> get_data_source(const bContext &C);
/**
* Get the ID of the table that should be displayed. This is used to look up the table from
* #SpaceSpreadsheet::tables.
*/
const SpreadsheetTableID *get_active_table_id(const SpaceSpreadsheet &sspreadsheet);
} // namespace blender::ed::spreadsheet

View File

@@ -4,9 +4,11 @@
#include <fmt/format.h>
#include "DNA_array_utils.hh"
#include "DNA_space_types.h"
#include "ED_screen.hh"
#include "ED_spreadsheet.hh"
#include "BLI_listbase.h"
#include "BLI_rect.h"
@@ -92,8 +94,8 @@ static wmOperatorStatus select_component_domain_invoke(bContext *C,
bke::AttrDomain domain = bke::AttrDomain(RNA_int_get(op->ptr, "attribute_domain_type"));
SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
sspreadsheet->geometry_component_type = uint8_t(component_type);
sspreadsheet->attribute_domain = uint8_t(domain);
sspreadsheet->geometry_id.geometry_component_type = uint8_t(component_type);
sspreadsheet->geometry_id.attribute_domain = uint8_t(domain);
/* Refresh header and main region. */
WM_main_add_notifier(NC_SPACE | ND_SPACE_SPREADSHEET, nullptr);
@@ -133,7 +135,9 @@ struct ResizeColumnData {
static wmOperatorStatus resize_column_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
ARegion &region = *CTX_wm_region(C);
SpaceSpreadsheet &sspreadsheet = *CTX_wm_space_spreadsheet(C);
SpreadsheetTable &table = *get_active_table(sspreadsheet);
ResizeColumnData &data = *static_cast<ResizeColumnData *>(op->customdata);
auto cancel = [&]() {
@@ -143,6 +147,7 @@ static wmOperatorStatus resize_column_modal(bContext *C, wmOperator *op, const w
return OPERATOR_CANCELLED;
};
auto finish = [&]() {
table.flag |= SPREADSHEET_TABLE_FLAG_MANUALLY_EDITED;
MEM_delete(&data);
ED_region_tag_redraw(&region);
return OPERATOR_FINISHED;
@@ -185,8 +190,12 @@ SpreadsheetColumn *find_hovered_column_edge(SpaceSpreadsheet &sspreadsheet,
ARegion &region,
const int2 &cursor_re)
{
SpreadsheetTable *table = get_active_table(sspreadsheet);
if (!table) {
return nullptr;
}
const float cursor_x_view = UI_view2d_region_to_view_x(&region.v2d, cursor_re.x);
LISTBASE_FOREACH (SpreadsheetColumn *, column, &sspreadsheet.columns) {
for (SpreadsheetColumn *column : Span{table->columns, table->num_columns}) {
if (std::abs(cursor_x_view - column->runtime->right_x) < SPREADSHEET_EDGE_ACTION_ZONE) {
return column;
}
@@ -198,8 +207,12 @@ SpreadsheetColumn *find_hovered_column(SpaceSpreadsheet &sspreadsheet,
ARegion &region,
const int2 &cursor_re)
{
SpreadsheetTable *table = get_active_table(sspreadsheet);
if (!table) {
return nullptr;
}
const float cursor_x_view = UI_view2d_region_to_view_x(&region.v2d, cursor_re.x);
LISTBASE_FOREACH (SpreadsheetColumn *, column, &sspreadsheet.columns) {
for (SpreadsheetColumn *column : Span{table->columns, table->num_columns}) {
if (cursor_x_view > column->runtime->left_x && cursor_x_view <= column->runtime->right_x) {
return column;
}
@@ -281,6 +294,9 @@ static wmOperatorStatus fit_column_invoke(bContext *C, wmOperator * /*op*/, cons
return OPERATOR_CANCELLED;
}
SpreadsheetTable &table = *get_active_table(sspreadsheet);
table.flag |= SPREADSHEET_TABLE_FLAG_MANUALLY_EDITED;
const float width_px = values->fit_column_width_px();
column->width = width_px / SPREADSHEET_WIDTH_UNIT;
@@ -323,6 +339,9 @@ static wmOperatorStatus reorder_columns_invoke(bContext *C, wmOperator *op, cons
WM_cursor_set(CTX_wm_window(C), WM_CURSOR_HAND_CLOSED);
SpreadsheetTable *table = get_active_table(sspreadsheet);
const int old_index = Span{table->columns, table->num_columns}.first_index(column_to_move);
ReorderColumnData *data = MEM_new<ReorderColumnData>(__func__);
data->column = column_to_move;
data->initial_cursor_x_view = UI_view2d_region_to_view_x(&region.v2d, cursor_re.x);
@@ -330,8 +349,8 @@ static wmOperatorStatus reorder_columns_invoke(bContext *C, wmOperator *op, cons
ReorderColumnVisualizationData &visualization_data =
sspreadsheet.runtime->reorder_column_visualization_data.emplace();
visualization_data.column_to_move = column_to_move;
visualization_data.new_prev_column = column_to_move->prev;
visualization_data.old_index = old_index;
visualization_data.new_index = old_index;
visualization_data.current_offset_x_px = 0;
UI_view2d_edge_pan_init(C, &data->pan_data, 0, 0, 1, 26, 0.5f, 0.0f);
@@ -353,26 +372,22 @@ static wmOperatorStatus reorder_columns_modal(bContext *C, wmOperator *op, const
const int2 cursor_re{event->mval[0], event->mval[1]};
ReorderColumnData &data = *static_cast<ReorderColumnData *>(op->customdata);
/* Detect the column that we want to insert to on the right. If it ends up being null, the column
* is inserted in the beginning. */
SpreadsheetColumn *new_prev_column = nullptr;
SpreadsheetTable &table = *get_active_table(sspreadsheet);
Span<SpreadsheetColumn *> columns(table.columns, table.num_columns);
const int old_index = columns.first_index(data.column);
int new_index = 0;
SpreadsheetColumn *hovered_column = find_hovered_column(sspreadsheet, region, cursor_re);
if (hovered_column) {
const int moved_column_index = BLI_findindex(&sspreadsheet.columns, data.column);
const int hovered_column_index = BLI_findindex(&sspreadsheet.columns, hovered_column);
if (hovered_column_index <= moved_column_index) {
new_prev_column = hovered_column->prev;
}
else {
new_prev_column = hovered_column;
}
new_index = columns.first_index(hovered_column);
}
else {
if (cursor_re.x > sspreadsheet.runtime->left_column_width) {
new_prev_column = static_cast<SpreadsheetColumn *>(sspreadsheet.columns.last);
if (new_prev_column == data.column) {
new_prev_column = new_prev_column->prev;
}
new_index = columns.size() - 1;
}
else {
new_index = 0;
}
}
@@ -391,10 +406,10 @@ static wmOperatorStatus reorder_columns_modal(bContext *C, wmOperator *op, const
return OPERATOR_CANCELLED;
}
case LEFTMOUSE: {
if (new_prev_column != data.column->prev) {
BLI_remlink(&sspreadsheet.columns, data.column);
BLI_insertlinkafter(&sspreadsheet.columns, new_prev_column, data.column);
if (old_index != new_index) {
dna::array::move_index(table.columns, table.num_columns, old_index, new_index);
}
table.flag |= SPREADSHEET_TABLE_FLAG_MANUALLY_EDITED;
cleanup_on_finish();
return OPERATOR_FINISHED;
}
@@ -403,7 +418,7 @@ static wmOperatorStatus reorder_columns_modal(bContext *C, wmOperator *op, const
ReorderColumnVisualizationData &visualization_data =
*sspreadsheet.runtime->reorder_column_visualization_data;
visualization_data.new_prev_column = new_prev_column;
visualization_data.new_index = new_index;
visualization_data.current_offset_x_px = UI_view2d_region_to_view_x(&region.v2d,
cursor_re.x) -
data.initial_cursor_x_view;

View File

@@ -24,6 +24,8 @@
#include "WM_api.hh"
#include "WM_types.hh"
#include "ED_spreadsheet.hh"
#include "spreadsheet_intern.hh"
#include "spreadsheet_row_filter_ui.hh"
@@ -115,10 +117,14 @@ static std::string value_string(const SpreadsheetRowFilter &row_filter,
return "";
}
static SpreadsheetColumn *lookup_visible_column_for_filter(const SpaceSpreadsheet &sspreadsheet,
const StringRef column_name)
static const SpreadsheetColumn *lookup_visible_column_for_filter(
const SpaceSpreadsheet &sspreadsheet, const StringRef column_name)
{
LISTBASE_FOREACH (SpreadsheetColumn *, column, &sspreadsheet.columns) {
const SpreadsheetTable *table = get_active_table(sspreadsheet);
if (!table) {
return nullptr;
}
for (const SpreadsheetColumn *column : Span{table->columns, table->num_columns}) {
if (column->display_name == column_name) {
return column;
}

View File

@@ -0,0 +1,306 @@
/* SPDX-FileCopyrightText: 2025 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BKE_viewer_path.hh"
#include "BLI_hash.hh"
#include "BLI_listbase.h"
#include "DNA_array_utils.hh"
#include "BLO_read_write.hh"
#include "spreadsheet_column.hh"
#include "spreadsheet_table.hh"
uint64_t SpreadsheetTableID::hash() const
{
switch (eSpreadsheetTableIDType(this->type)) {
case SPREADSHEET_TABLE_ID_TYPE_GEOMETRY: {
const auto &self = *reinterpret_cast<const SpreadsheetTableIDGeometry *>(this);
const uint64_t viewer_path_hash = BKE_viewer_path_hash(self.viewer_path);
const uint64_t instance_ids_hash = get_default_hash(
blender::Span(self.instance_ids, self.instance_ids_num));
return blender::get_default_hash(
blender::get_default_hash(this->type, viewer_path_hash, self.geometry_component_type),
blender::get_default_hash(
self.attribute_domain, self.object_eval_state, self.layer_index, instance_ids_hash));
}
}
return uint64_t(this->type);
}
bool operator==(const SpreadsheetTableID &a, const SpreadsheetTableID &b)
{
if (a.type != b.type) {
return false;
}
const eSpreadsheetTableIDType type = eSpreadsheetTableIDType(a.type);
switch (type) {
case SPREADSHEET_TABLE_ID_TYPE_GEOMETRY: {
const auto &a_ = *reinterpret_cast<const SpreadsheetTableIDGeometry *>(&a);
const auto &b_ = *reinterpret_cast<const SpreadsheetTableIDGeometry *>(&b);
return BKE_viewer_path_equal(&a_.viewer_path, &b_.viewer_path) &&
a_.geometry_component_type == b_.geometry_component_type &&
a_.attribute_domain == b_.attribute_domain &&
a_.object_eval_state == b_.object_eval_state && a_.layer_index == b_.layer_index &&
blender::Span(a_.instance_ids, a_.instance_ids_num) ==
blender::Span(b_.instance_ids, b_.instance_ids_num);
}
}
return false;
}
bool operator!=(const SpreadsheetTableID &a, const SpreadsheetTableID &b)
{
return !(a == b);
}
namespace blender::ed::spreadsheet {
SpreadsheetTableIDGeometry *spreadsheet_table_id_new_geometry()
{
auto *table_id = MEM_callocN<SpreadsheetTableIDGeometry>(__func__);
table_id->base.type = SPREADSHEET_TABLE_ID_TYPE_GEOMETRY;
return table_id;
}
void spreadsheet_table_id_copy_content_geometry(SpreadsheetTableIDGeometry &dst,
const SpreadsheetTableIDGeometry &src)
{
BKE_viewer_path_copy(&dst.viewer_path, &src.viewer_path);
dst.geometry_component_type = src.geometry_component_type;
dst.attribute_domain = src.attribute_domain;
dst.object_eval_state = src.object_eval_state;
dst.layer_index = src.layer_index;
dst.instance_ids = static_cast<SpreadsheetInstanceID *>(MEM_dupallocN(src.instance_ids));
dst.instance_ids_num = src.instance_ids_num;
}
SpreadsheetTableID *spreadsheet_table_id_copy(const SpreadsheetTableID &src_table_id)
{
switch (eSpreadsheetTableIDType(src_table_id.type)) {
case SPREADSHEET_TABLE_ID_TYPE_GEOMETRY: {
const auto &src = *reinterpret_cast<const SpreadsheetTableIDGeometry *>(&src_table_id);
auto *new_table_id = spreadsheet_table_id_new_geometry();
spreadsheet_table_id_copy_content_geometry(*new_table_id, src);
return &new_table_id->base;
}
}
return nullptr;
}
void spreadsheet_table_id_free_content(SpreadsheetTableID *table_id)
{
switch (eSpreadsheetTableIDType(table_id->type)) {
case SPREADSHEET_TABLE_ID_TYPE_GEOMETRY: {
auto *table_id_ = reinterpret_cast<SpreadsheetTableIDGeometry *>(table_id);
BKE_viewer_path_clear(&table_id_->viewer_path);
MEM_SAFE_FREE(table_id_->instance_ids);
break;
}
}
}
void spreadsheet_table_id_free(SpreadsheetTableID *table_id)
{
spreadsheet_table_id_free_content(table_id);
MEM_freeN(table_id);
}
void spreadsheet_table_id_blend_write_content_geometry(BlendWriter *writer,
const SpreadsheetTableIDGeometry *table_id)
{
BKE_viewer_path_blend_write(writer, &table_id->viewer_path);
BLO_write_struct_array(
writer, SpreadsheetInstanceID, table_id->instance_ids_num, table_id->instance_ids);
}
void spreadsheet_table_id_blend_write(BlendWriter *writer, const SpreadsheetTableID *table_id)
{
switch (eSpreadsheetTableIDType(table_id->type)) {
case SPREADSHEET_TABLE_ID_TYPE_GEOMETRY: {
const auto *table_id_ = reinterpret_cast<const SpreadsheetTableIDGeometry *>(table_id);
BLO_write_struct(writer, SpreadsheetTableIDGeometry, table_id_);
spreadsheet_table_id_blend_write_content_geometry(writer, table_id_);
break;
}
}
}
void spreadsheet_table_id_blend_read(BlendDataReader *reader, SpreadsheetTableID *table_id)
{
switch (eSpreadsheetTableIDType(table_id->type)) {
case SPREADSHEET_TABLE_ID_TYPE_GEOMETRY: {
auto *table_id_ = reinterpret_cast<SpreadsheetTableIDGeometry *>(table_id);
BKE_viewer_path_blend_read_data(reader, &table_id_->viewer_path);
BLO_read_struct_array(
reader, SpreadsheetInstanceID, table_id_->instance_ids_num, &table_id_->instance_ids);
break;
}
}
}
void spreadsheet_table_id_remap_id(SpreadsheetTableID &table_id,
const bke::id::IDRemapper &mappings)
{
switch (eSpreadsheetTableIDType(table_id.type)) {
case SPREADSHEET_TABLE_ID_TYPE_GEOMETRY: {
auto *table_id_ = reinterpret_cast<SpreadsheetTableIDGeometry *>(&table_id);
BKE_viewer_path_id_remap(&table_id_->viewer_path, mappings);
break;
}
}
}
void spreadsheet_table_id_foreach_id(SpreadsheetTableID &table_id, LibraryForeachIDData *data)
{
switch (eSpreadsheetTableIDType(table_id.type)) {
case SPREADSHEET_TABLE_ID_TYPE_GEOMETRY: {
auto *table_id_ = reinterpret_cast<SpreadsheetTableIDGeometry *>(&table_id);
BKE_viewer_path_foreach_id(data, &table_id_->viewer_path);
break;
}
}
}
SpreadsheetTable *spreadsheet_table_new(SpreadsheetTableID *table_id)
{
SpreadsheetTable *spreadsheet_table = MEM_callocN<SpreadsheetTable>(__func__);
spreadsheet_table->id = table_id;
return spreadsheet_table;
}
SpreadsheetTable *spreadsheet_table_copy(const SpreadsheetTable &src_table)
{
SpreadsheetTable *new_table = spreadsheet_table_new(spreadsheet_table_id_copy(*src_table.id));
new_table->num_columns = src_table.num_columns;
new_table->columns = MEM_calloc_arrayN<SpreadsheetColumn *>(src_table.num_columns, __func__);
for (const int i : IndexRange(src_table.num_columns)) {
new_table->columns[i] = spreadsheet_column_copy(src_table.columns[i]);
}
return new_table;
}
void spreadsheet_table_free(SpreadsheetTable *table)
{
spreadsheet_table_id_free(table->id);
for (const int i : IndexRange(table->num_columns)) {
spreadsheet_column_free(table->columns[i]);
}
MEM_SAFE_FREE(table->columns);
MEM_freeN(table);
}
void spreadsheet_table_blend_write(BlendWriter *writer, const SpreadsheetTable *table)
{
BLO_write_struct(writer, SpreadsheetTable, table);
spreadsheet_table_id_blend_write(writer, table->id);
BLO_write_pointer_array(writer, table->num_columns, table->columns);
for (const int i : IndexRange(table->num_columns)) {
spreadsheet_column_blend_write(writer, table->columns[i]);
}
}
void spreadsheet_table_blend_read(BlendDataReader *reader, SpreadsheetTable *table)
{
BLO_read_struct(reader, SpreadsheetTableID, &table->id);
spreadsheet_table_id_blend_read(reader, table->id);
BLO_read_pointer_array(reader, table->num_columns, reinterpret_cast<void **>(&table->columns));
for (const int i : IndexRange(table->num_columns)) {
BLO_read_struct(reader, SpreadsheetColumn, &table->columns[i]);
spreadsheet_column_blend_read(reader, table->columns[i]);
}
}
void spreadsheet_table_remap_id(SpreadsheetTable &table, const bke::id::IDRemapper &mappings)
{
spreadsheet_table_id_remap_id(*table.id, mappings);
}
void spreadsheet_table_foreach_id(SpreadsheetTable &table, LibraryForeachIDData *data)
{
spreadsheet_table_id_foreach_id(*table.id, data);
}
SpreadsheetTable *spreadsheet_table_find(SpaceSpreadsheet &sspreadsheet,
const SpreadsheetTableID &table_id)
{
return const_cast<SpreadsheetTable *>(
spreadsheet_table_find(const_cast<const SpaceSpreadsheet &>(sspreadsheet), table_id));
}
const SpreadsheetTable *spreadsheet_table_find(const SpaceSpreadsheet &sspreadsheet,
const SpreadsheetTableID &table_id)
{
for (const SpreadsheetTable *table : Span{sspreadsheet.tables, sspreadsheet.num_tables}) {
if (*table->id == table_id) {
return table;
}
}
return nullptr;
}
void spreadsheet_table_add(SpaceSpreadsheet &sspreadsheet, SpreadsheetTable *table)
{
SpreadsheetTable **new_tables = MEM_calloc_arrayN<SpreadsheetTable *>(
sspreadsheet.num_tables + 1, __func__);
std::copy_n(sspreadsheet.tables, sspreadsheet.num_tables, new_tables);
new_tables[sspreadsheet.num_tables] = table;
MEM_SAFE_FREE(sspreadsheet.tables);
sspreadsheet.tables = new_tables;
sspreadsheet.num_tables++;
}
void spreadsheet_table_remove_unused(SpaceSpreadsheet &sspreadsheet)
{
uint32_t min_last_used = 0;
const int max_tables = 50;
if (sspreadsheet.num_tables > max_tables) {
Vector<uint32_t> last_used_times;
for (const SpreadsheetTable *table : Span(sspreadsheet.tables, sspreadsheet.num_tables)) {
last_used_times.append(table->last_used);
}
std::sort(last_used_times.begin(), last_used_times.end());
min_last_used = last_used_times[sspreadsheet.num_tables - max_tables];
}
dna::array::remove_if<SpreadsheetTable *>(
&sspreadsheet.tables,
&sspreadsheet.num_tables,
[&](const SpreadsheetTable *table) {
if (!(table->flag & SPREADSHEET_TABLE_FLAG_MANUALLY_EDITED)) {
/* Remove tables that have never been modified manually. Those can be rebuilt from
* scratch if necessary. */
return true;
}
if (table->last_used < min_last_used) {
/* The table has not been used for a while and there are too many unused tables. So
* garbage collect this table. This does remove user-edited column widths and orders, but
* doesn't remove any actual data. */
return true;
}
switch (eSpreadsheetTableIDType(table->id->type)) {
case SPREADSHEET_TABLE_ID_TYPE_GEOMETRY: {
const SpreadsheetTableIDGeometry &table_id =
*reinterpret_cast<const SpreadsheetTableIDGeometry *>(table->id);
LISTBASE_FOREACH (ViewerPathElem *, elem, &table_id.viewer_path.path) {
if (elem->type == VIEWER_PATH_ELEM_TYPE_ID) {
const IDViewerPathElem &id_elem = reinterpret_cast<const IDViewerPathElem &>(
*elem);
if (!id_elem.id) {
/* Remove tables which reference an ID that does not exist anymore. */
return true;
}
}
}
break;
}
}
return false;
},
[](SpreadsheetTable **table) { spreadsheet_table_free(*table); });
}
} // namespace blender::ed::spreadsheet

View File

@@ -0,0 +1,44 @@
/* SPDX-FileCopyrightText: 2025 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "BKE_lib_remap.hh"
#include "DNA_space_types.h"
namespace blender::ed::spreadsheet {
SpreadsheetTableIDGeometry *spreadsheet_table_id_new_geometry();
SpreadsheetTableID *spreadsheet_table_id_copy(const SpreadsheetTableID &src_table_id);
void spreadsheet_table_id_copy_content_geometry(SpreadsheetTableIDGeometry &dst,
const SpreadsheetTableIDGeometry &src);
void spreadsheet_table_id_free(SpreadsheetTableID *table_id);
void spreadsheet_table_id_free_content(SpreadsheetTableID *table_id);
void spreadsheet_table_id_blend_write(BlendWriter *writer, const SpreadsheetTableID *table_id);
void spreadsheet_table_id_blend_write_content_geometry(BlendWriter *writer,
const SpreadsheetTableIDGeometry *table_id);
void spreadsheet_table_id_blend_read(BlendDataReader *reader, SpreadsheetTableID *table_id);
void spreadsheet_table_id_remap_id(SpreadsheetTableID &table_id,
const bke::id::IDRemapper &mappings);
void spreadsheet_table_id_foreach_id(SpreadsheetTableID &table_id, LibraryForeachIDData *data);
SpreadsheetTable *spreadsheet_table_new(SpreadsheetTableID *table_id);
SpreadsheetTable *spreadsheet_table_copy(const SpreadsheetTable &src_table);
void spreadsheet_table_free(SpreadsheetTable *table);
void spreadsheet_table_blend_write(BlendWriter *writer, const SpreadsheetTable *table);
void spreadsheet_table_blend_read(BlendDataReader *reader, SpreadsheetTable *table);
void spreadsheet_table_remap_id(SpreadsheetTable &table, const bke::id::IDRemapper &mappings);
void spreadsheet_table_foreach_id(SpreadsheetTable &table, LibraryForeachIDData *data);
SpreadsheetTable *spreadsheet_table_find(SpaceSpreadsheet &sspreadsheet,
const SpreadsheetTableID &table_id);
const SpreadsheetTable *spreadsheet_table_find(const SpaceSpreadsheet &sspreadsheet,
const SpreadsheetTableID &table_id);
void spreadsheet_table_add(SpaceSpreadsheet &sspreadsheet, SpreadsheetTable *table);
void spreadsheet_table_remove_unused(SpaceSpreadsheet &sspreadsheet);
void spreadsheet_table_remove_if(SpaceSpreadsheet &sspreadsheet,
FunctionRef<bool(SpreadsheetTable &)> predicate);
} // namespace blender::ed::spreadsheet

View File

@@ -159,7 +159,7 @@ void activate_geometry_node(Main &bmain, SpaceNode &snode, bNode &node)
if (sl->spacetype == SPACE_SPREADSHEET) {
SpaceSpreadsheet &sspreadsheet = *reinterpret_cast<SpaceSpreadsheet *>(sl);
if (!(sspreadsheet.flag & SPREADSHEET_FLAG_PINNED)) {
sspreadsheet.object_eval_state = SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE;
sspreadsheet.geometry_id.object_eval_state = SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE;
}
}
else if (sl->spacetype == SPACE_VIEW3D) {

View File

@@ -12,6 +12,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_function_ref.hh"
#include "BLI_index_range.hh"
#include "BLI_utildefines.h"
@@ -51,6 +52,28 @@ inline void remove_index(
}
}
/**
* Removes all elements for which the predicate is true. The remaining ones stay in the order they
* were in before.
*/
template<typename T>
inline void remove_if(T **items,
int *items_num,
FunctionRef<bool(const T &)> predicate,
void (*destruct_item)(T *))
{
static_assert(std::is_trivial_v<T>);
/* This sorts the items-to-remove to the back. */
const int remaining = std::partition(*items,
*items + *items_num,
[&](const T &value) { return !predicate(value); }) -
*items;
for (const int i : IndexRange::from_begin_end(remaining, *items_num)) {
destruct_item(&(*items)[i]);
}
*items_num = remaining;
}
/**
* Removes all elements from an array and frees it.
*/

View File

@@ -1019,6 +1019,19 @@ typedef enum eSpreadsheetColumnValueType {
SPREADSHEET_VALUE_TYPE_FLOAT4X4 = 12,
} eSpreadsheetColumnValueType;
typedef enum eSpreadsheetTableIDType {
/** This table uses the #SpreadsheetTableIDGeometry key. */
SPREADSHEET_TABLE_ID_TYPE_GEOMETRY = 0,
} eSpreadsheetTableType;
typedef enum eSpreadsheetTableFlag {
/**
* If a generated table has never been manually edited (e.g. changing column order), it can be
* discarded when it's no longer displayed.
*/
SPREADSHEET_TABLE_FLAG_MANUALLY_EDITED = (1 << 0),
} eSpreadsheetTableFlag;
/**
* We can't just use UI_UNIT_X, because it does not take `widget.points` into account, which
* modifies the width of text as well.

View File

@@ -1079,7 +1079,6 @@ typedef struct SpreadsheetColumnID {
} SpreadsheetColumnID;
typedef struct SpreadsheetColumn {
struct SpreadsheetColumn *next, *prev;
/**
* Identifies the data in the column.
* This is a pointer instead of a struct to make it easier if we want to "subclass"
@@ -1107,8 +1106,66 @@ typedef struct SpreadsheetColumn {
typedef struct SpreadsheetInstanceID {
int reference_index;
#ifdef __cplusplus
uint64_t hash() const;
friend bool operator==(const SpreadsheetInstanceID &a, const SpreadsheetInstanceID &b);
friend bool operator!=(const SpreadsheetInstanceID &a, const SpreadsheetInstanceID &b);
#endif
} SpreadsheetInstanceID;
typedef struct SpreadsheetTableID {
/** #eSpreadsheetTableIDType. */
int type;
#ifdef __cplusplus
uint64_t hash() const;
friend bool operator==(const SpreadsheetTableID &a, const SpreadsheetTableID &b);
friend bool operator!=(const SpreadsheetTableID &a, const SpreadsheetTableID &b);
#endif
} SpreadsheetTableID;
typedef struct SpreadsheetTableIDGeometry {
SpreadsheetTableID base;
char _pad0[4];
/**
* Context that is displayed in the editor. This is usually a either a single object (in
* original/evaluated mode) or path to a viewer node. This is retrieved from the workspace but
* can be pinned so that it stays constant even when the active node changes.
*/
ViewerPath viewer_path;
/**
* The "path" to the currently active instance reference. This is needed when viewing nested
* instances.
*/
SpreadsheetInstanceID *instance_ids;
int instance_ids_num;
/** #GeometryComponent::Type. */
uint8_t geometry_component_type;
/** #AttrDomain. */
uint8_t attribute_domain;
/** #eSpaceSpreadsheet_ObjectEvalState. */
uint8_t object_eval_state;
char _pad1[5];
/** Grease Pencil layer index for grease pencil component. */
int layer_index;
} SpreadsheetTableIDGeometry;
typedef struct SpreadsheetTable {
SpreadsheetTableID *id;
/** All the columns in the table. */
SpreadsheetColumn **columns;
int num_columns;
/** #eSpreadsheetTableFlag. */
uint32_t flag;
/**
* A logical time set when the table is used. This is used to be able to remove long-unused
* tables when there are too many. This is set from #SpaceSpreadsheet.table_use_clock.
*/
uint32_t last_used;
char _pad[4];
} SpreadsheetTable;
typedef struct SpaceSpreadsheet {
SpaceLink *next, *prev;
/** Storage of regions for inactive spaces. */
@@ -1118,40 +1175,27 @@ typedef struct SpaceSpreadsheet {
char _pad0[6];
/* End 'SpaceLink' header. */
/* List of #SpreadsheetColumn. */
ListBase columns;
/** The current table and persisted state of previously displayed tables. */
SpreadsheetTable **tables;
int num_tables;
char _pad1[3];
/* SpreadsheetRowFilter. */
ListBase row_filters;
/**
* Context that is currently displayed in the editor. This is usually a either a single object
* (in original/evaluated mode) or path to a viewer node. This is retrieved from the workspace
* but can be pinned so that it stays constant even when the active node changes.
*/
ViewerPath viewer_path;
/**
* The "path" to the currently active instance reference. This is needed when viewing nested
* instances.
*/
SpreadsheetInstanceID *instance_ids;
int instance_ids_num;
/* eSpaceSpreadsheet_FilterFlag. */
/** #eSpaceSpreadsheet_FilterFlag. */
uint8_t filter_flag;
/* #GeometryComponent::Type. */
uint8_t geometry_component_type;
/* #AttrDomain. */
uint8_t attribute_domain;
/* eSpaceSpreadsheet_ObjectEvalState. */
uint8_t object_eval_state;
/* Active grease pencil layer index for grease pencil component. */
int active_layer_index;
/** #SpreadsheetRowFilter. */
ListBase row_filters;
/** The currently active geometry data. This is used to look up the active table from #tables. */
SpreadsheetTableIDGeometry geometry_id;
/* eSpaceSpreadsheet_Flag. */
uint32_t flag;
/**
* This is increase whenver a new table is used. It allows for some garbage collection of
* long-unused tables when there are too many.
*/
uint32_t table_use_clock;
/** Index of the active viewer path element in the Data Source panel. */
int active_viewer_path_index;

View File

@@ -18,6 +18,7 @@
#include "ED_asset.hh"
#include "ED_buttons.hh"
#include "ED_spreadsheet.hh"
#include "BLI_string.h"
#include "BLI_sys_types.h"
@@ -547,6 +548,35 @@ static const EnumPropertyItem rna_enum_curve_display_handle_items[] = {
{0, nullptr, 0, nullptr, nullptr},
};
static const EnumPropertyItem spreadsheet_object_eval_state_items[] = {
{SPREADSHEET_OBJECT_EVAL_STATE_EVALUATED,
"EVALUATED",
ICON_NONE,
"Evaluated",
"Use data from fully or partially evaluated object"},
{SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL,
"ORIGINAL",
ICON_NONE,
"Original",
"Use data from original object without any modifiers applied"},
{SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE,
"VIEWER_NODE",
ICON_NONE,
"Viewer Node",
"Use intermediate data from viewer node"},
{0, nullptr, 0, nullptr, nullptr},
};
static const EnumPropertyItem spreadsheet_table_id_type_items[] = {
{SPREADSHEET_TABLE_ID_TYPE_GEOMETRY,
"GEOMETRY",
ICON_NONE,
"Geometry",
"Table contains geometry data"},
{0, nullptr, 0, nullptr, nullptr},
};
#ifdef RNA_RUNTIME
# include <algorithm>
@@ -3481,35 +3511,35 @@ static void rna_SpaceSpreadsheet_geometry_component_type_update(Main * /*bmain*/
{
using namespace blender;
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)ptr->data;
switch (sspreadsheet->geometry_component_type) {
switch (sspreadsheet->geometry_id.geometry_component_type) {
case int(bke::GeometryComponent::Type::Mesh): {
if (!ELEM(bke::AttrDomain(sspreadsheet->attribute_domain),
if (!ELEM(bke::AttrDomain(sspreadsheet->geometry_id.attribute_domain),
bke::AttrDomain::Point,
bke::AttrDomain::Edge,
bke::AttrDomain::Face,
bke::AttrDomain::Corner))
{
sspreadsheet->attribute_domain = uint8_t(bke::AttrDomain::Point);
sspreadsheet->geometry_id.attribute_domain = uint8_t(bke::AttrDomain::Point);
}
break;
}
case int(bke::GeometryComponent::Type::PointCloud): {
sspreadsheet->attribute_domain = uint8_t(bke::AttrDomain::Point);
sspreadsheet->geometry_id.attribute_domain = uint8_t(bke::AttrDomain::Point);
break;
}
case int(bke::GeometryComponent::Type::Instance): {
sspreadsheet->attribute_domain = uint8_t(bke::AttrDomain::Instance);
sspreadsheet->geometry_id.attribute_domain = uint8_t(bke::AttrDomain::Instance);
break;
}
case int(bke::GeometryComponent::Type::Volume): {
break;
}
case int(bke::GeometryComponent::Type::Curve): {
if (!ELEM(bke::AttrDomain(sspreadsheet->attribute_domain),
if (!ELEM(bke::AttrDomain(sspreadsheet->geometry_id.attribute_domain),
bke::AttrDomain::Point,
bke::AttrDomain::Curve))
{
sspreadsheet->attribute_domain = uint8_t(bke::AttrDomain::Point);
sspreadsheet->geometry_id.attribute_domain = uint8_t(bke::AttrDomain::Point);
}
break;
}
@@ -3523,8 +3553,9 @@ const EnumPropertyItem *rna_SpaceSpreadsheet_attribute_domain_itemf(bContext * /
{
using namespace blender;
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)ptr->data;
auto component_type = bke::GeometryComponent::Type(sspreadsheet->geometry_component_type);
if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL) {
auto component_type = bke::GeometryComponent::Type(
sspreadsheet->geometry_id.geometry_component_type);
if (sspreadsheet->geometry_id.object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL) {
ID *used_id = ed::spreadsheet::get_current_id(sspreadsheet);
if (used_id != nullptr) {
if (GS(used_id->name) == ID_OB) {
@@ -3582,6 +3613,56 @@ const EnumPropertyItem *rna_SpaceSpreadsheet_attribute_domain_itemf(bContext * /
return item_array;
}
static StructRNA *rna_SpreadsheetTableID_refine(PointerRNA *ptr)
{
SpreadsheetTableID *table_id = ptr->data_as<SpreadsheetTableID>();
switch (eSpreadsheetTableIDType(table_id->type)) {
case SPREADSHEET_TABLE_ID_TYPE_GEOMETRY:
return &RNA_SpreadsheetTableIDGeometry;
}
return &RNA_SpreadsheetTableID;
}
static void rna_iterator_SpreadsheetTable_columns_begin(CollectionPropertyIterator *iter,
PointerRNA *ptr)
{
SpreadsheetTable *table = ptr->data_as<SpreadsheetTable>();
rna_iterator_array_begin(
iter, ptr, table->columns, sizeof(SpreadsheetTable *), table->num_columns, 0, nullptr);
}
static int rna_iterator_SpreadsheetTable_columns_length(PointerRNA *ptr)
{
SpreadsheetTable *table = ptr->data_as<SpreadsheetTable>();
return table->num_columns;
}
static void rna_iterator_SpaceSpreadsheet_tables_begin(CollectionPropertyIterator *iter,
PointerRNA *ptr)
{
SpaceSpreadsheet *sspreadsheet = ptr->data_as<SpaceSpreadsheet>();
rna_iterator_array_begin(iter,
ptr,
sspreadsheet->tables,
sizeof(SpaceSpreadsheet *),
sspreadsheet->num_tables,
0,
nullptr);
}
static int rna_iterator_SpaceSpreadsheet_tables_length(PointerRNA *ptr)
{
SpaceSpreadsheet *sspreadsheet = ptr->data_as<SpaceSpreadsheet>();
return sspreadsheet->num_tables;
}
static PointerRNA rna_SpreadsheetTables_active_get(PointerRNA *ptr)
{
SpaceSpreadsheet *sspreadsheet = ptr->data_as<SpaceSpreadsheet>();
SpreadsheetTable *table = blender::ed::spreadsheet::get_active_table(*sspreadsheet);
return RNA_pointer_create_discrete(ptr->owner_id, &RNA_SpreadsheetTable, table);
}
static StructRNA *rna_viewer_path_elem_refine(PointerRNA *ptr)
{
ViewerPathElem *elem = static_cast<ViewerPathElem *>(ptr->data);
@@ -8478,6 +8559,102 @@ static void rna_def_spreadsheet_column(BlenderRNA *brna)
prop, "ID", "Data used to identify the corresponding data from the data source");
}
static void rna_def_spreadsheet_table_id(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "SpreadsheetTableID", nullptr);
RNA_def_struct_ui_text(
srna, "Spreadsheet Table ID", "Data used to identify a spreadsheet table");
RNA_def_struct_refine_func(srna, "rna_SpreadsheetTableID_refine");
prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, spreadsheet_table_id_type_items);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Type", "The type of the table identifier");
}
static void rna_def_spreadsheet_table_id_geometry(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
/* The properties below are read-only, because they are used as key for a table. */
srna = RNA_def_struct(brna, "SpreadsheetTableIDGeometry", "SpreadsheetTableID");
prop = RNA_def_property(srna, "object_eval_state", PROP_ENUM, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_enum_items(prop, spreadsheet_object_eval_state_items);
RNA_def_property_ui_text(prop, "Object Evaluation State", "");
prop = RNA_def_property(srna, "geometry_component_type", PROP_ENUM, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_enum_items(prop, rna_enum_geometry_component_type_items);
RNA_def_property_ui_text(
prop, "Geometry Component", "Part of the geometry to display data from");
prop = RNA_def_property(srna, "attribute_domain", PROP_ENUM, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_enum_items(prop, rna_enum_attribute_domain_items);
RNA_def_property_ui_text(prop, "Attribute Domain", "Attribute domain to display");
prop = RNA_def_property(srna, "viewer_path", PROP_POINTER, PROP_NONE);
RNA_def_property_ui_text(prop, "Viewer Path", "Path to the data that is displayed");
prop = RNA_def_property(srna, "layer_index", PROP_INT, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Layer Index", "Index of the Grease Pencil layer");
}
static void rna_def_spreadsheet_table(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
rna_def_spreadsheet_table_id(brna);
rna_def_spreadsheet_table_id_geometry(brna);
rna_def_spreadsheet_column(brna);
srna = RNA_def_struct(brna, "SpreadsheetTable", nullptr);
RNA_def_struct_ui_text(srna, "Spreadsheet Table", "Persistent data associated with a table");
prop = RNA_def_property(srna, "id", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "SpreadsheetTableID");
RNA_def_property_ui_text(prop, "ID", "Data used to identify the table");
prop = RNA_def_property(srna, "columns", PROP_COLLECTION, PROP_NONE);
RNA_def_property_struct_type(prop, "SpreadsheetColumn");
RNA_def_property_collection_funcs(prop,
"rna_iterator_SpreadsheetTable_columns_begin",
"rna_iterator_array_next",
"rna_iterator_array_end",
"rna_iterator_array_dereference_get",
"rna_iterator_SpreadsheetTable_columns_length",
nullptr,
nullptr,
nullptr);
RNA_def_property_ui_text(prop, "Columns", "Columns within the table");
}
static void rna_def_spreadsheet_tables(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "SpreadsheetTables", nullptr);
RNA_def_struct_sdna(srna, "SpaceSpreadsheet");
RNA_def_struct_ui_text(srna,
"Spreadsheet Tables",
"Active table and persisted state of previously displayed tables");
prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "SpreadsheetTable");
RNA_def_property_pointer_funcs(
prop, "rna_SpreadsheetTables_active_get", nullptr, nullptr, nullptr);
RNA_def_property_ui_text(prop, "Active Table", "");
}
static void rna_def_spreadsheet_row_filter(BlenderRNA *brna)
{
StructRNA *srna;
@@ -8723,24 +8900,8 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna)
PropertyRNA *prop;
StructRNA *srna;
static const EnumPropertyItem object_eval_state_items[] = {
{SPREADSHEET_OBJECT_EVAL_STATE_EVALUATED,
"EVALUATED",
ICON_NONE,
"Evaluated",
"Use data from fully or partially evaluated object"},
{SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL,
"ORIGINAL",
ICON_NONE,
"Original",
"Use data from original object without any modifiers applied"},
{SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE,
"VIEWER_NODE",
ICON_NONE,
"Viewer Node",
"Use intermediate data from viewer node"},
{0, nullptr, 0, nullptr, nullptr},
};
rna_def_spreadsheet_table(brna);
rna_def_spreadsheet_tables(brna);
srna = RNA_def_struct(brna, "SpaceSpreadsheet", "Space");
RNA_def_struct_ui_text(srna, "Space Spreadsheet", "Spreadsheet space data");
@@ -8760,6 +8921,7 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna)
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, nullptr);
prop = RNA_def_property(srna, "viewer_path", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, nullptr, "geometry_id.viewer_path");
RNA_def_property_ui_text(
prop, "Viewer Path", "Path to the data that is displayed in the spreadsheet");
@@ -8771,6 +8933,7 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna)
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, nullptr);
prop = RNA_def_property(srna, "geometry_component_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, nullptr, "geometry_id.geometry_component_type");
RNA_def_property_enum_items(prop, rna_enum_geometry_component_type_items);
RNA_def_property_ui_text(
prop, "Geometry Component", "Part of the geometry to display data from");
@@ -8779,6 +8942,7 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna)
"rna_SpaceSpreadsheet_geometry_component_type_update");
prop = RNA_def_property(srna, "attribute_domain", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, nullptr, "geometry_id.attribute_domain");
RNA_def_property_enum_items(prop, rna_enum_attribute_domain_items);
RNA_def_property_enum_funcs(
prop, nullptr, nullptr, "rna_SpaceSpreadsheet_attribute_domain_itemf");
@@ -8786,17 +8950,25 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna)
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, nullptr);
prop = RNA_def_property(srna, "object_eval_state", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, object_eval_state_items);
RNA_def_property_enum_sdna(prop, nullptr, "geometry_id.object_eval_state");
RNA_def_property_enum_items(prop, spreadsheet_object_eval_state_items);
RNA_def_property_ui_text(prop, "Object Evaluation State", "");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, nullptr);
rna_def_spreadsheet_column(brna);
prop = RNA_def_property(srna, "columns", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, nullptr, "columns", nullptr);
RNA_def_property_struct_type(prop, "SpreadsheetColumn");
RNA_def_property_ui_text(prop, "Columns", "Persistent data associated with spreadsheet columns");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, nullptr);
prop = RNA_def_property(srna, "tables", PROP_COLLECTION, PROP_NONE);
RNA_def_property_struct_type(prop, "SpreadsheetTable");
RNA_def_property_srna(prop, "SpreadsheetTables");
RNA_def_property_collection_funcs(prop,
"rna_iterator_SpaceSpreadsheet_tables_begin",
"rna_iterator_array_next",
"rna_iterator_array_end",
"rna_iterator_array_dereference_get",
"rna_iterator_SpaceSpreadsheet_tables_length",
nullptr,
nullptr,
nullptr);
RNA_def_property_ui_text(
prop, "Tables", "Persistent data for the tables shown in this spreadsheet editor");
rna_def_spreadsheet_row_filter(brna);

View File

@@ -848,7 +848,7 @@ static void find_side_effect_nodes(const NodesModifierData &nmd,
if (sl->spacetype == SPACE_SPREADSHEET) {
const SpaceSpreadsheet &sspreadsheet = *reinterpret_cast<const SpaceSpreadsheet *>(sl);
find_side_effect_nodes_for_viewer_path(
sspreadsheet.viewer_path, nmd, ctx, r_side_effect_nodes);
sspreadsheet.geometry_id.viewer_path, nmd, ctx, r_side_effect_nodes);
}
if (sl->spacetype == SPACE_VIEW3D) {
const View3D &v3d = *reinterpret_cast<const View3D *>(sl);