Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/space_spreadsheet')
-rw-r--r--source/blender/editors/space_spreadsheet/CMakeLists.txt18
-rw-r--r--source/blender/editors/space_spreadsheet/space_spreadsheet.cc251
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh58
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_column.cc72
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_column.hh48
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_column_layout.cc230
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_column_layout.hh115
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh92
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_context.cc306
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_context.hh (renamed from source/blender/editors/space_spreadsheet/spreadsheet_from_geometry.hh)15
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source.cc24
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh65
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc445
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh94
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_draw.cc18
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_draw.hh1
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_from_geometry.cc447
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_layout.cc256
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_layout.hh42
19 files changed, 1756 insertions, 841 deletions
diff --git a/source/blender/editors/space_spreadsheet/CMakeLists.txt b/source/blender/editors/space_spreadsheet/CMakeLists.txt
index a77e74ffd93..b2a0905d4a2 100644
--- a/source/blender/editors/space_spreadsheet/CMakeLists.txt
+++ b/source/blender/editors/space_spreadsheet/CMakeLists.txt
@@ -17,9 +17,9 @@
set(INC
../include
+ ../../blenfont
../../blenkernel
../../blenlib
- ../../blenfont
../../bmesh
../../depsgraph
../../functions
@@ -33,15 +33,23 @@ set(INC
set(SRC
space_spreadsheet.cc
- spreadsheet_column_layout.cc
+ spreadsheet_context.cc
+ spreadsheet_column.cc
+ spreadsheet_data_source.cc
+ spreadsheet_data_source_geometry.cc
spreadsheet_draw.cc
- spreadsheet_from_geometry.cc
+ spreadsheet_layout.cc
spreadsheet_ops.cc
+ spreadsheet_context.hh
+ spreadsheet_cell_value.hh
+ spreadsheet_column.hh
+ spreadsheet_column_values.hh
+ spreadsheet_data_source.hh
+ spreadsheet_data_source_geometry.hh
spreadsheet_draw.hh
- spreadsheet_column_layout.hh
- spreadsheet_from_geometry.hh
spreadsheet_intern.hh
+ spreadsheet_layout.hh
)
set(LIB
diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
index 0f7709b464e..1f0b5d5d13e 100644
--- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
+++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
@@ -17,12 +17,12 @@
#include <cstring>
#include "BLI_listbase.h"
-#include "BLI_resource_collector.hh"
#include "BKE_screen.h"
#include "ED_screen.h"
#include "ED_space_api.h"
+#include "ED_spreadsheet.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
@@ -41,12 +41,16 @@
#include "WM_api.h"
#include "WM_types.h"
+#include "BLF_api.h"
+
#include "spreadsheet_intern.hh"
-#include "spreadsheet_column_layout.hh"
-#include "spreadsheet_from_geometry.hh"
+#include "spreadsheet_context.hh"
+#include "spreadsheet_data_source_geometry.hh"
#include "spreadsheet_intern.hh"
+#include "spreadsheet_layout.hh"
+using namespace blender;
using namespace blender::ed::spreadsheet;
static SpaceLink *spreadsheet_create(const ScrArea *UNUSED(area), const Scene *UNUSED(scene))
@@ -85,6 +89,13 @@ static void spreadsheet_free(SpaceLink *sl)
{
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
MEM_SAFE_FREE(sspreadsheet->runtime);
+
+ LISTBASE_FOREACH_MUTABLE (SpreadsheetColumn *, column, &sspreadsheet->columns) {
+ spreadsheet_column_free(column);
+ }
+ LISTBASE_FOREACH_MUTABLE (SpreadsheetContext *, context, &sspreadsheet->context_path) {
+ spreadsheet_context_free(context);
+ }
}
static void spreadsheet_init(wmWindowManager *UNUSED(wm), ScrArea *area)
@@ -102,6 +113,18 @@ static SpaceLink *spreadsheet_duplicate(SpaceLink *sl)
SpaceSpreadsheet *sspreadsheet_new = (SpaceSpreadsheet *)MEM_dupallocN(sspreadsheet_old);
sspreadsheet_new->runtime = (SpaceSpreadsheet_Runtime *)MEM_dupallocN(sspreadsheet_old->runtime);
+ 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);
+ }
+
+ BLI_listbase_clear(&sspreadsheet_new->context_path);
+ LISTBASE_FOREACH_MUTABLE (SpreadsheetContext *, src_context, &sspreadsheet_old->context_path) {
+ SpreadsheetContext *new_context = spreadsheet_context_copy(src_context);
+ BLI_addtail(&sspreadsheet_new->context_path, new_context);
+ }
+
return (SpaceLink *)sspreadsheet_new;
}
@@ -109,6 +132,24 @@ static void spreadsheet_keymap(wmKeyConfig *UNUSED(keyconf))
{
}
+static void spreadsheet_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, ID *new_id)
+{
+ SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)slink;
+ LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) {
+ if (context->type == SPREADSHEET_CONTEXT_OBJECT) {
+ SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context;
+ if ((ID *)object_context->object == old_id) {
+ if (new_id && GS(new_id->name) == ID_OB) {
+ object_context->object = (Object *)new_id;
+ }
+ else {
+ object_context->object = nullptr;
+ }
+ }
+ }
+ }
+}
+
static void spreadsheet_main_region_init(wmWindowManager *wm, ARegion *region)
{
region->v2d.scroll = V2D_SCROLL_RIGHT | V2D_SCROLL_BOTTOM;
@@ -123,57 +164,212 @@ static void spreadsheet_main_region_init(wmWindowManager *wm, ARegion *region)
WM_event_add_keymap_handler(&region->handlers, keymap);
}
-static ID *get_used_id(const bContext *C)
+ID *ED_spreadsheet_get_current_id(struct SpaceSpreadsheet *sspreadsheet)
+{
+ if (BLI_listbase_is_empty(&sspreadsheet->context_path)) {
+ return nullptr;
+ }
+ SpreadsheetContext *root_context = (SpreadsheetContext *)sspreadsheet->context_path.first;
+ if (root_context->type != SPREADSHEET_CONTEXT_OBJECT) {
+ return nullptr;
+ }
+ SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)root_context;
+ return (ID *)object_context->object;
+}
+
+/* Check if the pinned context still exists. If it doesn't try to find a new context. */
+static void update_pinned_context_path_if_outdated(const bContext *C)
{
SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
- if (sspreadsheet->pinned_id != nullptr) {
- return sspreadsheet->pinned_id;
+
+ /* Currently, this only checks if the object has been deleted. In the future we can have a more
+ * sophisticated check for the entire context (including modifier and nodes). */
+ LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) {
+ if (context->type == SPREADSHEET_CONTEXT_OBJECT) {
+ SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context;
+ if (object_context->object == nullptr) {
+ ED_spreadsheet_context_path_clear(sspreadsheet);
+ break;
+ }
+ }
+ }
+ if (BLI_listbase_is_empty(&sspreadsheet->context_path)) {
+ Object *active_object = CTX_data_active_object(C);
+ if (active_object != nullptr) {
+ SpreadsheetContext *new_context = spreadsheet_context_new(SPREADSHEET_CONTEXT_OBJECT);
+ ((SpreadsheetContextObject *)new_context)->object = active_object;
+ BLI_addtail(&sspreadsheet->context_path, new_context);
+ }
+ }
+
+ if (BLI_listbase_is_empty(&sspreadsheet->context_path)) {
+ /* Don't pin empty context_path, that could be annoying. */
+ sspreadsheet->flag &= ~SPREADSHEET_FLAG_PINNED;
}
+}
+
+static void update_context_path_from_context(const bContext *C)
+{
+ SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
Object *active_object = CTX_data_active_object(C);
- return (ID *)active_object;
+ if (active_object == nullptr) {
+ ED_spreadsheet_context_path_clear(sspreadsheet);
+ return;
+ }
+ if (!BLI_listbase_is_empty(&sspreadsheet->context_path)) {
+ SpreadsheetContext *root_context = (SpreadsheetContext *)sspreadsheet->context_path.first;
+ if (root_context->type == SPREADSHEET_CONTEXT_OBJECT) {
+ SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)root_context;
+ if (object_context->object != active_object) {
+ ED_spreadsheet_context_path_clear(sspreadsheet);
+ }
+ }
+ }
+ if (BLI_listbase_is_empty(&sspreadsheet->context_path)) {
+ SpreadsheetContext *new_context = spreadsheet_context_new(SPREADSHEET_CONTEXT_OBJECT);
+ ((SpreadsheetContextObject *)new_context)->object = active_object;
+ BLI_addtail(&sspreadsheet->context_path, new_context);
+ }
}
-class FallbackSpreadsheetDrawer : public SpreadsheetDrawer {
-};
+static void update_context_path(const bContext *C)
+{
+ SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
+ if (sspreadsheet->flag & SPREADSHEET_FLAG_PINNED) {
+ update_pinned_context_path_if_outdated(C);
+ }
+ else {
+ update_context_path_from_context(C);
+ }
+}
-static void gather_spreadsheet_columns(const bContext *C,
- SpreadsheetColumnLayout &column_layout,
- blender::ResourceCollector &resources)
+static std::unique_ptr<DataSource> get_data_source(const bContext *C)
{
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
- ID *used_id = get_used_id(C);
+ SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
+ ID *used_id = ED_spreadsheet_get_current_id(sspreadsheet);
if (used_id == nullptr) {
- return;
+ return {};
}
const ID_Type id_type = GS(used_id->name);
if (id_type != ID_OB) {
- return;
+ return {};
}
Object *object_orig = (Object *)used_id;
- if (!ELEM(object_orig->type, OB_MESH, OB_POINTCLOUD)) {
- return;
+ if (!ELEM(object_orig->type, OB_MESH, OB_POINTCLOUD, OB_VOLUME)) {
+ return {};
}
Object *object_eval = DEG_get_evaluated_object(depsgraph, object_orig);
if (object_eval == nullptr) {
- return;
+ return {};
+ }
+
+ return data_source_from_geometry(C, object_eval);
+}
+
+static float get_column_width(const ColumnValues &values)
+{
+ if (values.default_width > 0) {
+ return values.default_width;
}
+ const int fontid = UI_style_get()->widget.uifont_id;
+ BLF_size(fontid, UI_DEFAULT_TEXT_POINTS, U.dpi);
+ const StringRefNull name = values.name();
+ const float name_width = BLF_width(fontid, name.data(), name.size());
+ return std::max<float>(name_width / UI_UNIT_X + 1.0f, 3.0f);
+}
- return spreadsheet_columns_from_geometry(C, object_eval, column_layout, resources);
+static float get_column_width_in_pixels(const ColumnValues &values)
+{
+ return get_column_width(values) * SPREADSHEET_WIDTH_UNIT;
+}
+
+static int get_index_column_width(const int tot_rows)
+{
+ const int fontid = UI_style_get()->widget.uifont_id;
+ BLF_size(fontid, UI_style_get_dpi()->widget.points * U.pixelsize, U.dpi);
+ return std::to_string(std::max(0, tot_rows - 1)).size() * BLF_width(fontid, "0", 1) +
+ UI_UNIT_X * 0.75;
+}
+
+static void update_visible_columns(ListBase &columns, 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;
+ }
+ }
+
+ data_source.foreach_default_column_ids([&](const SpreadsheetColumnID &column_id) {
+ 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);
+ BLI_addtail(&columns, new_column);
+ }
+ }
+ });
}
static void spreadsheet_main_region_draw(const bContext *C, ARegion *region)
{
SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
+ update_context_path(C);
- blender::ResourceCollector resources;
- SpreadsheetColumnLayout column_layout;
- gather_spreadsheet_columns(C, column_layout, resources);
+ std::unique_ptr<DataSource> data_source = get_data_source(C);
+ if (!data_source) {
+ data_source = std::make_unique<DataSource>();
+ }
+
+ update_visible_columns(sspreadsheet->columns, *data_source);
+
+ SpreadsheetLayout spreadsheet_layout;
+ ResourceScope scope;
+
+ LISTBASE_FOREACH (SpreadsheetColumn *, column, &sspreadsheet->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);
+ const ColumnValues *values = scope.add(std::move(values_ptr), __func__);
+ const int width = get_column_width_in_pixels(*values);
+ spreadsheet_layout.columns.append({values, width});
+ }
+
+ const int tot_rows = data_source->tot_rows();
+ spreadsheet_layout.index_column_width = get_index_column_width(tot_rows);
+ spreadsheet_layout.row_indices = IndexRange(tot_rows).as_span();
+
+ if (const GeometryDataSource *geometry_data_source = dynamic_cast<const GeometryDataSource *>(
+ data_source.get())) {
+ Object *object_eval = geometry_data_source->object_eval();
+ Object *object_orig = DEG_get_original_object(object_eval);
+ if (object_orig->type == OB_MESH) {
+ if (object_orig->mode == OB_MODE_EDIT) {
+ if (sspreadsheet->filter_flag & SPREADSHEET_FILTER_SELECTED_ONLY) {
+ spreadsheet_layout.row_indices = geometry_data_source->get_selected_element_indices();
+ }
+ }
+ }
+ }
- sspreadsheet->runtime->visible_rows = column_layout.row_indices.size();
- sspreadsheet->runtime->tot_columns = column_layout.columns.size();
- sspreadsheet->runtime->tot_rows = column_layout.tot_rows;
+ sspreadsheet->runtime->tot_columns = spreadsheet_layout.columns.size();
+ sspreadsheet->runtime->tot_rows = tot_rows;
+ sspreadsheet->runtime->visible_rows = spreadsheet_layout.row_indices.size();
- std::unique_ptr<SpreadsheetDrawer> drawer = spreadsheet_drawer_from_column_layout(column_layout);
+ std::unique_ptr<SpreadsheetDrawer> drawer = spreadsheet_drawer_from_layout(spreadsheet_layout);
draw_spreadsheet_in_region(C, region, *drawer);
/* Tag footer for redraw, because the main region updates data for the footer. */
@@ -208,6 +404,7 @@ static void spreadsheet_main_region_listener(const wmRegionListenerParams *param
}
break;
}
+ case NC_TEXTURE:
case NC_GEOM: {
ED_region_tag_redraw(region);
break;
@@ -222,6 +419,7 @@ static void spreadsheet_header_region_init(wmWindowManager *UNUSED(wm), ARegion
static void spreadsheet_header_region_draw(const bContext *C, ARegion *region)
{
+ update_context_path(C);
ED_region_header(C, region);
}
@@ -327,6 +525,7 @@ void ED_spacetype_spreadsheet(void)
st->duplicate = spreadsheet_duplicate;
st->operatortypes = spreadsheet_operatortypes;
st->keymap = spreadsheet_keymap;
+ st->id_remap = spreadsheet_id_remap;
/* regions: main window */
art = (ARegionType *)MEM_callocN(sizeof(ARegionType), "spacetype spreadsheet region");
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh
new file mode 100644
index 00000000000..c9b73aabf96
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh
@@ -0,0 +1,58 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include <optional>
+
+#include "BLI_color.hh"
+#include "BLI_float2.hh"
+#include "BLI_float3.hh"
+
+struct Object;
+struct Collection;
+
+namespace blender::ed::spreadsheet {
+
+struct ObjectCellValue {
+ const Object *object;
+};
+
+struct CollectionCellValue {
+ const Collection *collection;
+};
+
+/**
+ * This is a type that can hold the value of a cell in a spreadsheet. This type allows us to
+ * decouple the drawing of individual cells from the code that generates the data to be displayed.
+ */
+class CellValue {
+ public:
+ /* The implementation just uses a bunch of `std::option` for now. Unfortunately, we cannot use
+ * `std::variant` yet, due to missing compiler support. This type can really be optimized more,
+ * but it does not really matter too much currently. */
+
+ std::optional<int> value_int;
+ std::optional<float> value_float;
+ std::optional<bool> value_bool;
+ std::optional<float2> value_float2;
+ std::optional<float3> value_float3;
+ std::optional<ColorGeometry4f> value_color;
+ std::optional<ObjectCellValue> value_object;
+ std::optional<CollectionCellValue> value_collection;
+};
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column.cc b/source/blender/editors/space_spreadsheet/spreadsheet_column.cc
new file mode 100644
index 00000000000..de40545fdae
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_column.cc
@@ -0,0 +1,72 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "DNA_space_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_hash.hh"
+#include "BLI_string.h"
+#include "BLI_string_ref.hh"
+
+#include "spreadsheet_column.hh"
+
+namespace blender::ed::spreadsheet {
+
+SpreadsheetColumnID *spreadsheet_column_id_new()
+{
+ SpreadsheetColumnID *column_id = (SpreadsheetColumnID *)MEM_callocN(sizeof(SpreadsheetColumnID),
+ __func__);
+ return column_id;
+}
+
+SpreadsheetColumnID *spreadsheet_column_id_copy(const SpreadsheetColumnID *src_column_id)
+{
+ SpreadsheetColumnID *new_column_id = spreadsheet_column_id_new();
+ new_column_id->name = BLI_strdup(src_column_id->name);
+ return new_column_id;
+}
+
+void spreadsheet_column_id_free(SpreadsheetColumnID *column_id)
+{
+ if (column_id->name != nullptr) {
+ MEM_freeN(column_id->name);
+ }
+ MEM_freeN(column_id);
+}
+
+SpreadsheetColumn *spreadsheet_column_new(SpreadsheetColumnID *column_id)
+{
+ SpreadsheetColumn *column = (SpreadsheetColumn *)MEM_callocN(sizeof(SpreadsheetColumn),
+ __func__);
+ column->id = column_id;
+ return column;
+}
+
+SpreadsheetColumn *spreadsheet_column_copy(const SpreadsheetColumn *src_column)
+{
+ SpreadsheetColumnID *new_column_id = spreadsheet_column_id_copy(src_column->id);
+ SpreadsheetColumn *new_column = spreadsheet_column_new(new_column_id);
+ return new_column;
+}
+
+void spreadsheet_column_free(SpreadsheetColumn *column)
+{
+ spreadsheet_column_id_free(column->id);
+ MEM_freeN(column);
+}
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column.hh b/source/blender/editors/space_spreadsheet/spreadsheet_column.hh
new file mode 100644
index 00000000000..bb245851d55
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_column.hh
@@ -0,0 +1,48 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include "DNA_space_types.h"
+
+#include "BLI_hash.hh"
+
+namespace blender {
+template<> struct DefaultHash<SpreadsheetColumnID> {
+ uint64_t operator()(const SpreadsheetColumnID &column_id) const
+ {
+ return get_default_hash(StringRef(column_id.name));
+ }
+};
+} // namespace blender
+
+inline bool operator==(const SpreadsheetColumnID &a, const SpreadsheetColumnID &b)
+{
+ using blender::StringRef;
+ return StringRef(a.name) == StringRef(b.name);
+}
+
+namespace blender::ed::spreadsheet {
+
+SpreadsheetColumnID *spreadsheet_column_id_new();
+SpreadsheetColumnID *spreadsheet_column_id_copy(const SpreadsheetColumnID *src_column_id);
+void spreadsheet_column_id_free(SpreadsheetColumnID *column_id);
+
+SpreadsheetColumn *spreadsheet_column_new(SpreadsheetColumnID *column_id);
+SpreadsheetColumn *spreadsheet_column_copy(const SpreadsheetColumn *src_column);
+void spreadsheet_column_free(SpreadsheetColumn *column);
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_column_layout.cc
deleted file mode 100644
index 46760c0dd4e..00000000000
--- a/source/blender/editors/space_spreadsheet/spreadsheet_column_layout.cc
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#include <iomanip>
-#include <sstream>
-
-#include "spreadsheet_column_layout.hh"
-
-#include "DNA_userdef_types.h"
-
-#include "UI_interface.h"
-#include "UI_resources.h"
-
-#include "BLF_api.h"
-
-namespace blender::ed::spreadsheet {
-
-class ColumnLayoutDrawer : public SpreadsheetDrawer {
- private:
- const SpreadsheetColumnLayout &column_layout_;
- Vector<int> column_widths_;
-
- public:
- ColumnLayoutDrawer(const SpreadsheetColumnLayout &column_layout) : column_layout_(column_layout)
- {
- tot_columns = column_layout.columns.size();
- tot_rows = column_layout.row_indices.size();
-
- const int fontid = UI_style_get()->widget.uifont_id;
- /* Use a consistent font size for the width calculation. */
- BLF_size(fontid, 11 * U.pixelsize, U.dpi);
-
- /* The width of the index column depends on the maximum row index. */
- left_column_width = std::to_string(std::max(0, column_layout_.tot_rows - 1)).size() *
- BLF_width(fontid, "0", 1) +
- UI_UNIT_X * 0.75;
-
- /* The column widths depend on the column name widths. */
- const int minimum_column_width = 3 * UI_UNIT_X;
- const int header_name_padding = UI_UNIT_X;
- for (const SpreadsheetColumn *column : column_layout_.columns) {
- if (column->default_width == 0.0f) {
- StringRefNull name = column->name();
- const int name_width = BLF_width(fontid, name.data(), name.size());
- const int width = std::max(name_width + header_name_padding, minimum_column_width);
- column_widths_.append(width);
- }
- else {
- column_widths_.append(column->default_width * UI_UNIT_X);
- }
- }
- }
-
- void draw_top_row_cell(int column_index, const CellDrawParams &params) const final
- {
- const StringRefNull name = column_layout_.columns[column_index]->name();
- 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 &params) const final
- {
- const int real_index = column_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.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 &params) const final
- {
- const int real_index = column_layout_.row_indices[row_index];
- const SpreadsheetColumn &column = *column_layout_.columns[column_index];
- CellValue cell_value;
- column.get_value(real_index, cell_value);
-
- if (cell_value.value_int.has_value()) {
- const int value = *cell_value.value_int;
- const std::string value_str = std::to_string(value);
- 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);
- }
- else if (cell_value.value_float.has_value()) {
- const float value = *cell_value.value_float;
- std::stringstream ss;
- ss << std::fixed << std::setprecision(3) << value;
- const std::string value_str = ss.str();
- 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);
- }
- else if (cell_value.value_bool.has_value()) {
- const bool value = *cell_value.value_bool;
- const int icon = value ? ICON_CHECKBOX_HLT : ICON_CHECKBOX_DEHLT;
- uiDefIconTextBut(params.block,
- UI_BTYPE_LABEL,
- 0,
- icon,
- "",
- params.xmin,
- params.ymin,
- params.width,
- params.height,
- nullptr,
- 0,
- 0,
- 0,
- 0,
- nullptr);
- }
- else if (cell_value.value_object.has_value()) {
- const ObjectCellValue value = *cell_value.value_object;
- uiDefIconTextBut(params.block,
- UI_BTYPE_LABEL,
- 0,
- ICON_OBJECT_DATA,
- reinterpret_cast<const ID *const>(value.object)->name + 2,
- params.xmin,
- params.ymin,
- params.width,
- params.height,
- nullptr,
- 0,
- 0,
- 0,
- 0,
- nullptr);
- }
- else if (cell_value.value_collection.has_value()) {
- const CollectionCellValue value = *cell_value.value_collection;
- uiDefIconTextBut(params.block,
- UI_BTYPE_LABEL,
- 0,
- ICON_OUTLINER_COLLECTION,
- reinterpret_cast<const ID *const>(value.collection)->name + 2,
- params.xmin,
- params.ymin,
- params.width,
- params.height,
- nullptr,
- 0,
- 0,
- 0,
- 0,
- nullptr);
- }
- }
-
- int column_width(int column_index) const final
- {
- return column_widths_[column_index];
- }
-};
-
-std::unique_ptr<SpreadsheetDrawer> spreadsheet_drawer_from_column_layout(
- const SpreadsheetColumnLayout &column_layout)
-{
- return std::make_unique<ColumnLayoutDrawer>(column_layout);
-}
-
-} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column_layout.hh b/source/blender/editors/space_spreadsheet/spreadsheet_column_layout.hh
deleted file mode 100644
index 611337df007..00000000000
--- a/source/blender/editors/space_spreadsheet/spreadsheet_column_layout.hh
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#pragma once
-
-#include <optional>
-
-#include "spreadsheet_draw.hh"
-
-struct Object;
-struct Collection;
-
-namespace blender::ed::spreadsheet {
-
-struct ObjectCellValue {
- const Object *object;
-};
-
-struct CollectionCellValue {
- const Collection *collection;
-};
-
-/**
- * This is a small type that can hold the value of a cell in a spreadsheet. This type allows us to
- * decouple the drawing of individual cells from the code that generates the data to be displayed.
- */
-class CellValue {
- public:
- /* The implementation just uses a bunch of `std::option` for now. Unfortunately, we cannot use
- * `std::variant` yet, due to missing compiler support. This type can really be optimized more,
- * but it does not really matter too much currently. */
-
- std::optional<int> value_int;
- std::optional<float> value_float;
- std::optional<bool> value_bool;
- std::optional<ObjectCellValue> value_object;
- std::optional<CollectionCellValue> value_collection;
-};
-
-/**
- * This represents a column in a spreadsheet. It has a name and provides a value for all the cells
- * in the column.
- */
-class SpreadsheetColumn {
- protected:
- std::string name_;
-
- public:
- SpreadsheetColumn(std::string name) : name_(std::move(name))
- {
- }
-
- virtual ~SpreadsheetColumn() = default;
-
- virtual void get_value(int index, CellValue &r_cell_value) const = 0;
-
- StringRefNull name() const
- {
- return name_;
- }
-
- /* The default width of newly created columns, in UI units. */
- float default_width = 0.0f;
-};
-
-/* Utility class for the function below. */
-template<typename GetValueF> class LambdaSpreadsheetColumn : public SpreadsheetColumn {
- private:
- GetValueF get_value_;
-
- public:
- LambdaSpreadsheetColumn(std::string name, GetValueF get_value)
- : SpreadsheetColumn(std::move(name)), get_value_(std::move(get_value))
- {
- }
-
- void get_value(int index, CellValue &r_cell_value) const final
- {
- get_value_(index, r_cell_value);
- }
-};
-
-/* Utility function that simplifies creating a spreadsheet column from a lambda function. */
-template<typename GetValueF>
-std::unique_ptr<SpreadsheetColumn> spreadsheet_column_from_function(std::string name,
- GetValueF get_value)
-{
- return std::make_unique<LambdaSpreadsheetColumn<GetValueF>>(std::move(name),
- std::move(get_value));
-}
-
-/* This contains information required to create a spreadsheet drawer from columns. */
-struct SpreadsheetColumnLayout {
- Vector<const SpreadsheetColumn *> columns;
- Span<int64_t> row_indices;
- int tot_rows = 0;
-};
-
-std::unique_ptr<SpreadsheetDrawer> spreadsheet_drawer_from_column_layout(
- const SpreadsheetColumnLayout &column_layout);
-
-} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh b/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh
new file mode 100644
index 00000000000..373c988a41c
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh
@@ -0,0 +1,92 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include "BLI_string_ref.hh"
+
+#include "spreadsheet_cell_value.hh"
+
+namespace blender::ed::spreadsheet {
+
+/**
+ * This represents a column in a spreadsheet. It has a name and provides a value for all the cells
+ * in the column.
+ */
+class ColumnValues {
+ protected:
+ std::string name_;
+ int size_;
+
+ public:
+ ColumnValues(std::string name, const int size) : name_(std::move(name)), size_(size)
+ {
+ }
+
+ virtual ~ColumnValues() = default;
+
+ virtual void get_value(int index, CellValue &r_cell_value) const = 0;
+
+ StringRefNull name() const
+ {
+ return name_;
+ }
+
+ int size() const
+ {
+ return size_;
+ }
+
+ /* The default width of newly created columns, in UI units. */
+ float default_width = 0.0f;
+};
+
+/* Utility class for the function below. */
+template<typename GetValueF> class LambdaColumnValues : public ColumnValues {
+ private:
+ GetValueF get_value_;
+
+ public:
+ LambdaColumnValues(std::string name, int size, GetValueF get_value)
+ : ColumnValues(std::move(name), size), get_value_(std::move(get_value))
+ {
+ }
+
+ void get_value(int index, CellValue &r_cell_value) const final
+ {
+ get_value_(index, r_cell_value);
+ }
+};
+
+/* Utility function that simplifies creating a spreadsheet column from a lambda function. */
+template<typename GetValueF>
+std::unique_ptr<ColumnValues> column_values_from_function(std::string name,
+ const int size,
+ GetValueF get_value,
+ const float default_width = 0.0f)
+{
+ std::unique_ptr<ColumnValues> column_values = std::make_unique<LambdaColumnValues<GetValueF>>(
+ std::move(name), size, std::move(get_value));
+ column_values->default_width = default_width;
+ return column_values;
+}
+
+static constexpr float default_float_column_width = 3;
+static constexpr float default_float2_column_width = 2 * default_float_column_width;
+static constexpr float default_float3_column_width = 3 * default_float_column_width;
+static constexpr float default_color_column_width = 4 * default_float_column_width;
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_context.cc b/source/blender/editors/space_spreadsheet/spreadsheet_context.cc
new file mode 100644
index 00000000000..3eb43338908
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_context.cc
@@ -0,0 +1,306 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_hash.h"
+#include "BLI_hash.hh"
+#include "BLI_hash_mm2a.h"
+#include "BLI_listbase.h"
+#include "BLI_string.h"
+#include "BLI_utildefines.h"
+#include "BLI_vector.hh"
+
+#include "ED_spreadsheet.h"
+
+#include "DEG_depsgraph.h"
+
+#include "BKE_main.h"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
+
+#include "spreadsheet_context.hh"
+
+namespace blender::ed::spreadsheet {
+
+static SpreadsheetContextObject *spreadsheet_context_object_new()
+{
+ SpreadsheetContextObject *context = (SpreadsheetContextObject *)MEM_callocN(
+ sizeof(SpreadsheetContextObject), __func__);
+ context->base.type = SPREADSHEET_CONTEXT_OBJECT;
+ return context;
+}
+
+static SpreadsheetContextObject *spreadsheet_context_object_copy(
+ const SpreadsheetContextObject *src_context)
+{
+ SpreadsheetContextObject *new_context = spreadsheet_context_object_new();
+ new_context->object = src_context->object;
+ return new_context;
+}
+
+static void spreadsheet_context_object_hash(const SpreadsheetContextObject *context,
+ BLI_HashMurmur2A *mm2)
+{
+ BLI_hash_mm2a_add(mm2, (const uchar *)&context->object, sizeof(Object *));
+}
+
+static void spreadsheet_context_object_free(SpreadsheetContextObject *context)
+{
+ MEM_freeN(context);
+}
+
+static SpreadsheetContextModifier *spreadsheet_context_modifier_new()
+{
+ SpreadsheetContextModifier *context = (SpreadsheetContextModifier *)MEM_callocN(
+ sizeof(SpreadsheetContextModifier), __func__);
+ context->base.type = SPREADSHEET_CONTEXT_MODIFIER;
+ return context;
+}
+
+static SpreadsheetContextModifier *spreadsheet_context_modifier_copy(
+ const SpreadsheetContextModifier *src_context)
+{
+ SpreadsheetContextModifier *new_context = spreadsheet_context_modifier_new();
+ if (src_context->modifier_name) {
+ new_context->modifier_name = BLI_strdup(src_context->modifier_name);
+ }
+ return new_context;
+}
+
+static void spreadsheet_context_modifier_hash(const SpreadsheetContextModifier *context,
+ BLI_HashMurmur2A *mm2)
+{
+ if (context->modifier_name) {
+ BLI_hash_mm2a_add(mm2, (const uchar *)context->modifier_name, strlen(context->modifier_name));
+ }
+}
+
+static void spreadsheet_context_modifier_free(SpreadsheetContextModifier *context)
+{
+ if (context->modifier_name) {
+ MEM_freeN(context->modifier_name);
+ }
+ MEM_freeN(context);
+}
+
+static SpreadsheetContextNode *spreadsheet_context_node_new()
+{
+ SpreadsheetContextNode *context = (SpreadsheetContextNode *)MEM_callocN(
+ sizeof(SpreadsheetContextNode), __func__);
+ context->base.type = SPREADSHEET_CONTEXT_NODE;
+ return context;
+}
+
+static SpreadsheetContextNode *spreadsheet_context_node_copy(
+ const SpreadsheetContextNode *src_context)
+{
+ SpreadsheetContextNode *new_context = spreadsheet_context_node_new();
+ if (src_context->node_name) {
+ new_context->node_name = BLI_strdup(src_context->node_name);
+ }
+ return new_context;
+}
+
+static void spreadsheet_context_node_hash(const SpreadsheetContextNode *context,
+ BLI_HashMurmur2A *mm2)
+{
+ if (context->node_name) {
+ BLI_hash_mm2a_add(mm2, (const uchar *)context->node_name, strlen(context->node_name));
+ }
+}
+
+static void spreadsheet_context_node_free(SpreadsheetContextNode *context)
+{
+ if (context->node_name) {
+ MEM_freeN(context->node_name);
+ }
+ MEM_freeN(context);
+}
+
+SpreadsheetContext *spreadsheet_context_new(eSpaceSpreadsheet_ContextType type)
+{
+ switch (type) {
+ case SPREADSHEET_CONTEXT_OBJECT: {
+ return (SpreadsheetContext *)spreadsheet_context_object_new();
+ }
+ case SPREADSHEET_CONTEXT_MODIFIER: {
+ return (SpreadsheetContext *)spreadsheet_context_modifier_new();
+ }
+ case SPREADSHEET_CONTEXT_NODE: {
+ return (SpreadsheetContext *)spreadsheet_context_node_new();
+ }
+ }
+ BLI_assert_unreachable();
+ return nullptr;
+}
+
+SpreadsheetContext *spreadsheet_context_copy(const SpreadsheetContext *old_context)
+{
+ switch (old_context->type) {
+ case SPREADSHEET_CONTEXT_OBJECT: {
+ return (SpreadsheetContext *)spreadsheet_context_object_copy(
+ (const SpreadsheetContextObject *)old_context);
+ }
+ case SPREADSHEET_CONTEXT_MODIFIER: {
+ return (SpreadsheetContext *)spreadsheet_context_modifier_copy(
+ (const SpreadsheetContextModifier *)old_context);
+ }
+ case SPREADSHEET_CONTEXT_NODE: {
+ return (SpreadsheetContext *)spreadsheet_context_node_copy(
+ (const SpreadsheetContextNode *)old_context);
+ }
+ }
+ BLI_assert_unreachable();
+ return nullptr;
+}
+
+static void spreadsheet_context_hash(const SpreadsheetContext *context, BLI_HashMurmur2A *mm2)
+{
+ BLI_hash_mm2a_add_int(mm2, context->type);
+ switch (context->type) {
+ case SPREADSHEET_CONTEXT_OBJECT: {
+ spreadsheet_context_object_hash((const SpreadsheetContextObject *)context, mm2);
+ break;
+ }
+ case SPREADSHEET_CONTEXT_MODIFIER: {
+ spreadsheet_context_modifier_hash((const SpreadsheetContextModifier *)context, mm2);
+ break;
+ }
+ case SPREADSHEET_CONTEXT_NODE: {
+ spreadsheet_context_node_hash((const SpreadsheetContextNode *)context, mm2);
+ break;
+ }
+ }
+}
+
+void spreadsheet_context_free(SpreadsheetContext *context)
+{
+ switch (context->type) {
+ case SPREADSHEET_CONTEXT_OBJECT: {
+ return spreadsheet_context_object_free((SpreadsheetContextObject *)context);
+ }
+ case SPREADSHEET_CONTEXT_MODIFIER: {
+ return spreadsheet_context_modifier_free((SpreadsheetContextModifier *)context);
+ }
+ case SPREADSHEET_CONTEXT_NODE: {
+ return spreadsheet_context_node_free((SpreadsheetContextNode *)context);
+ }
+ }
+ BLI_assert_unreachable();
+}
+
+/**
+ * Tag any data relevant to the spreadsheet's context for recalculation in order to collect
+ * information to display in the editor, which may be cached during evaluation.
+ */
+static void spreadsheet_context_update_tag(SpaceSpreadsheet *sspreadsheet)
+{
+ using namespace blender;
+ Vector<const SpreadsheetContext *> context_path = sspreadsheet->context_path;
+ if (context_path.is_empty()) {
+ return;
+ }
+ if (context_path[0]->type != SPREADSHEET_CONTEXT_OBJECT) {
+ return;
+ }
+ SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context_path[0];
+ Object *object = object_context->object;
+ if (object == nullptr) {
+ return;
+ }
+ if (context_path.size() == 1) {
+ /* No need to reevaluate, when the final or original object is viewed. */
+ return;
+ }
+
+ DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY);
+}
+
+} // namespace blender::ed::spreadsheet
+
+SpreadsheetContext *ED_spreadsheet_context_new(int type)
+{
+ return blender::ed::spreadsheet::spreadsheet_context_new((eSpaceSpreadsheet_ContextType)type);
+}
+
+void ED_spreadsheet_context_free(struct SpreadsheetContext *context)
+{
+ blender::ed::spreadsheet::spreadsheet_context_free(context);
+}
+
+void ED_spreadsheet_context_path_clear(struct SpaceSpreadsheet *sspreadsheet)
+{
+ LISTBASE_FOREACH_MUTABLE (SpreadsheetContext *, context, &sspreadsheet->context_path) {
+ ED_spreadsheet_context_free(context);
+ }
+ BLI_listbase_clear(&sspreadsheet->context_path);
+}
+
+void ED_spreadsheet_context_path_update_tag(SpaceSpreadsheet *sspreadsheet)
+{
+ blender::ed::spreadsheet::spreadsheet_context_update_tag(sspreadsheet);
+}
+
+uint64_t ED_spreadsheet_context_path_hash(SpaceSpreadsheet *sspreadsheet)
+{
+ BLI_HashMurmur2A mm2;
+ BLI_hash_mm2a_init(&mm2, 1234);
+ LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) {
+ blender::ed::spreadsheet::spreadsheet_context_hash(context, &mm2);
+ }
+ return BLI_hash_mm2a_end(&mm2);
+}
+
+void ED_spreadsheet_set_geometry_node_context(struct SpaceSpreadsheet *sspreadsheet,
+ struct SpaceNode *snode,
+ struct bNode *node)
+{
+ using namespace blender::ed::spreadsheet;
+ ED_spreadsheet_context_path_clear(sspreadsheet);
+
+ Object *object = (Object *)snode->id;
+ ModifierData *modifier = BKE_object_active_modifier(object);
+
+ {
+ SpreadsheetContextObject *context = spreadsheet_context_object_new();
+ context->object = object;
+ BLI_addtail(&sspreadsheet->context_path, context);
+ }
+ {
+ SpreadsheetContextModifier *context = spreadsheet_context_modifier_new();
+ context->modifier_name = BLI_strdup(modifier->name);
+ BLI_addtail(&sspreadsheet->context_path, context);
+ }
+ {
+ int i;
+ LISTBASE_FOREACH_INDEX (bNodeTreePath *, path, &snode->treepath, i) {
+ if (i == 0) {
+ continue;
+ }
+ SpreadsheetContextNode *context = spreadsheet_context_node_new();
+ context->node_name = BLI_strdup(path->node_name);
+ BLI_addtail(&sspreadsheet->context_path, context);
+ }
+ }
+ {
+ SpreadsheetContextNode *context = spreadsheet_context_node_new();
+ context->node_name = BLI_strdup(node->name);
+ BLI_addtail(&sspreadsheet->context_path, context);
+ }
+
+ sspreadsheet->object_eval_state = SPREADSHEET_OBJECT_EVAL_STATE_EVALUATED;
+}
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_from_geometry.hh b/source/blender/editors/space_spreadsheet/spreadsheet_context.hh
index cef731517b9..d71769e42b3 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_from_geometry.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_context.hh
@@ -16,19 +16,12 @@
#pragma once
-#include "BKE_geometry_set.hh"
-
-#include "BLI_resource_collector.hh"
-
-#include "spreadsheet_column_layout.hh"
-
-struct bContext;
+#include "DNA_space_types.h"
namespace blender::ed::spreadsheet {
-void spreadsheet_columns_from_geometry(const bContext *C,
- Object *object_eval,
- SpreadsheetColumnLayout &column_layout,
- ResourceCollector &resources);
+SpreadsheetContext *spreadsheet_context_new(eSpaceSpreadsheet_ContextType type);
+SpreadsheetContext *spreadsheet_context_copy(const SpreadsheetContext *old_context);
+void spreadsheet_context_free(SpreadsheetContext *context);
} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source.cc
new file mode 100644
index 00000000000..09b8c6b1b54
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source.cc
@@ -0,0 +1,24 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "spreadsheet_data_source.hh"
+
+namespace blender::ed::spreadsheet {
+
+/* Provide a "key function" for the linker. */
+DataSource::~DataSource() = default;
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh b/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh
new file mode 100644
index 00000000000..de47109a144
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh
@@ -0,0 +1,65 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include "BLI_function_ref.hh"
+
+#include "spreadsheet_column.hh"
+#include "spreadsheet_column_values.hh"
+
+namespace blender::ed::spreadsheet {
+
+/**
+ * This class is subclassed to implement different data sources for the spreadsheet. A data source
+ * provides the information that should be displayed. It is not concerned with how data is laid
+ * out in the spreadsheet editor exactly.
+ */
+class DataSource {
+ public:
+ virtual ~DataSource();
+
+ /**
+ * Calls the callback with all the column ids that should be displayed as long as the user does
+ * not manually add or remove columns. The column id can be stack allocated. Therefore, the
+ * callback should not keep a reference to it (and copy it instead).
+ */
+ virtual void foreach_default_column_ids(FunctionRef<void(const SpreadsheetColumnID &)> fn) const
+ {
+ UNUSED_VARS(fn);
+ }
+
+ /**
+ * Returns the column values the given column id. If no data exists for this id, null is
+ * returned.
+ */
+ virtual std::unique_ptr<ColumnValues> get_column_values(
+ const SpreadsheetColumnID &column_id) const
+ {
+ UNUSED_VARS(column_id);
+ return {};
+ }
+
+ /**
+ * Returns the number of rows in columns returned by #get_column_values.
+ */
+ virtual int tot_rows() const
+ {
+ return 0;
+ }
+};
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
new file mode 100644
index 00000000000..452885959f6
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
@@ -0,0 +1,445 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BKE_context.h"
+#include "BKE_editmesh.h"
+#include "BKE_lib_id.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_wrapper.h"
+#include "BKE_modifier.h"
+
+#include "DNA_ID.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_space_types.h"
+#include "DNA_userdef_types.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "ED_spreadsheet.h"
+
+#include "bmesh.h"
+
+#include "spreadsheet_data_source_geometry.hh"
+#include "spreadsheet_intern.hh"
+
+namespace blender::ed::spreadsheet {
+
+void GeometryDataSource::foreach_default_column_ids(
+ FunctionRef<void(const SpreadsheetColumnID &)> fn) const
+{
+ component_->attribute_foreach([&](StringRefNull name, const AttributeMetaData &meta_data) {
+ if (meta_data.domain != domain_) {
+ return true;
+ }
+ SpreadsheetColumnID column_id;
+ column_id.name = (char *)name.c_str();
+ fn(column_id);
+ return true;
+ });
+}
+
+std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
+ const SpreadsheetColumnID &column_id) const
+{
+ std::lock_guard lock{mutex_};
+
+ bke::ReadAttributeLookup attribute = component_->attribute_try_get_for_read(column_id.name);
+ if (!attribute) {
+ return {};
+ }
+ const fn::GVArray *varray = scope_.add(std::move(attribute.varray), __func__);
+ if (attribute.domain != domain_) {
+ return {};
+ }
+ int domain_size = varray->size();
+ const CustomDataType type = bke::cpp_type_to_custom_data_type(varray->type());
+ switch (type) {
+ case CD_PROP_FLOAT:
+ return column_values_from_function(
+ column_id.name, domain_size, [varray](int index, CellValue &r_cell_value) {
+ float value;
+ varray->get(index, &value);
+ r_cell_value.value_float = value;
+ });
+ case CD_PROP_INT32:
+ return column_values_from_function(
+ column_id.name, domain_size, [varray](int index, CellValue &r_cell_value) {
+ int value;
+ varray->get(index, &value);
+ r_cell_value.value_int = value;
+ });
+ case CD_PROP_BOOL:
+ return column_values_from_function(
+ column_id.name, domain_size, [varray](int index, CellValue &r_cell_value) {
+ bool value;
+ varray->get(index, &value);
+ r_cell_value.value_bool = value;
+ });
+ case CD_PROP_FLOAT2: {
+ return column_values_from_function(
+ column_id.name,
+ domain_size,
+ [varray](int index, CellValue &r_cell_value) {
+ float2 value;
+ varray->get(index, &value);
+ r_cell_value.value_float2 = value;
+ },
+ default_float2_column_width);
+ }
+ case CD_PROP_FLOAT3: {
+ return column_values_from_function(
+ column_id.name,
+ domain_size,
+ [varray](int index, CellValue &r_cell_value) {
+ float3 value;
+ varray->get(index, &value);
+ r_cell_value.value_float3 = value;
+ },
+ default_float3_column_width);
+ }
+ case CD_PROP_COLOR: {
+ return column_values_from_function(
+ column_id.name,
+ domain_size,
+ [varray](int index, CellValue &r_cell_value) {
+ ColorGeometry4f value;
+ varray->get(index, &value);
+ r_cell_value.value_color = value;
+ },
+ default_color_column_width);
+ }
+ default:
+ break;
+ }
+ return {};
+}
+
+int GeometryDataSource::tot_rows() const
+{
+ return component_->attribute_domain_size(domain_);
+}
+
+using IsVertexSelectedFn = FunctionRef<bool(int vertex_index)>;
+
+static void get_selected_vertex_indices(const Mesh &mesh,
+ const IsVertexSelectedFn is_vertex_selected_fn,
+ Vector<int64_t> &r_vertex_indices)
+{
+ for (const int i : IndexRange(mesh.totvert)) {
+ if (is_vertex_selected_fn(i)) {
+ r_vertex_indices.append(i);
+ }
+ }
+}
+
+static void get_selected_corner_indices(const Mesh &mesh,
+ const IsVertexSelectedFn is_vertex_selected_fn,
+ Vector<int64_t> &r_corner_indices)
+{
+ for (const int i : IndexRange(mesh.totloop)) {
+ const MLoop &loop = mesh.mloop[i];
+ if (is_vertex_selected_fn(loop.v)) {
+ r_corner_indices.append(i);
+ }
+ }
+}
+
+static void get_selected_face_indices(const Mesh &mesh,
+ const IsVertexSelectedFn is_vertex_selected_fn,
+ Vector<int64_t> &r_face_indices)
+{
+ for (const int poly_index : IndexRange(mesh.totpoly)) {
+ const MPoly &poly = mesh.mpoly[poly_index];
+ bool is_selected = true;
+ for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
+ const MLoop &loop = mesh.mloop[loop_index];
+ if (!is_vertex_selected_fn(loop.v)) {
+ is_selected = false;
+ break;
+ }
+ }
+ if (is_selected) {
+ r_face_indices.append(poly_index);
+ }
+ }
+}
+
+static void get_selected_edge_indices(const Mesh &mesh,
+ const IsVertexSelectedFn is_vertex_selected_fn,
+ Vector<int64_t> &r_edge_indices)
+{
+ for (const int i : IndexRange(mesh.totedge)) {
+ const MEdge &edge = mesh.medge[i];
+ if (is_vertex_selected_fn(edge.v1) && is_vertex_selected_fn(edge.v2)) {
+ r_edge_indices.append(i);
+ }
+ }
+}
+
+static void get_selected_indices_on_domain(const Mesh &mesh,
+ const AttributeDomain domain,
+ const IsVertexSelectedFn is_vertex_selected_fn,
+ Vector<int64_t> &r_indices)
+{
+ switch (domain) {
+ case ATTR_DOMAIN_POINT:
+ return get_selected_vertex_indices(mesh, is_vertex_selected_fn, r_indices);
+ case ATTR_DOMAIN_FACE:
+ return get_selected_face_indices(mesh, is_vertex_selected_fn, r_indices);
+ case ATTR_DOMAIN_CORNER:
+ return get_selected_corner_indices(mesh, is_vertex_selected_fn, r_indices);
+ case ATTR_DOMAIN_EDGE:
+ return get_selected_edge_indices(mesh, is_vertex_selected_fn, r_indices);
+ default:
+ return;
+ }
+}
+
+Span<int64_t> GeometryDataSource::get_selected_element_indices() const
+{
+ std::lock_guard lock{mutex_};
+
+ BLI_assert(object_eval_->mode == OB_MODE_EDIT);
+ BLI_assert(component_->type() == GEO_COMPONENT_TYPE_MESH);
+ Object *object_orig = DEG_get_original_object(object_eval_);
+ Vector<int64_t> &indices = scope_.construct<Vector<int64_t>>(__func__);
+ const MeshComponent *mesh_component = static_cast<const MeshComponent *>(component_);
+ const Mesh *mesh_eval = mesh_component->get_for_read();
+ Mesh *mesh_orig = (Mesh *)object_orig->data;
+ BMesh *bm = mesh_orig->edit_mesh->bm;
+ BM_mesh_elem_table_ensure(bm, BM_VERT);
+
+ int *orig_indices = (int *)CustomData_get_layer(&mesh_eval->vdata, CD_ORIGINDEX);
+ if (orig_indices != nullptr) {
+ /* Use CD_ORIGINDEX layer if it exists. */
+ auto is_vertex_selected = [&](int vertex_index) -> bool {
+ const int i_orig = orig_indices[vertex_index];
+ if (i_orig < 0) {
+ return false;
+ }
+ if (i_orig >= bm->totvert) {
+ return false;
+ }
+ BMVert *vert = bm->vtable[i_orig];
+ return BM_elem_flag_test(vert, BM_ELEM_SELECT);
+ };
+ get_selected_indices_on_domain(*mesh_eval, domain_, is_vertex_selected, indices);
+ }
+ else if (mesh_eval->totvert == bm->totvert) {
+ /* Use a simple heuristic to match original vertices to evaluated ones. */
+ auto is_vertex_selected = [&](int vertex_index) -> bool {
+ BMVert *vert = bm->vtable[vertex_index];
+ return BM_elem_flag_test(vert, BM_ELEM_SELECT);
+ };
+ get_selected_indices_on_domain(*mesh_eval, domain_, is_vertex_selected, indices);
+ }
+
+ return indices;
+}
+
+void InstancesDataSource::foreach_default_column_ids(
+ FunctionRef<void(const SpreadsheetColumnID &)> fn) const
+{
+ if (component_->instances_amount() == 0) {
+ return;
+ }
+
+ SpreadsheetColumnID column_id;
+ column_id.name = (char *)"Name";
+ fn(column_id);
+ for (const char *name : {"Position", "Rotation", "Scale", "ID"}) {
+ column_id.name = (char *)name;
+ fn(column_id);
+ }
+}
+
+std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values(
+ const SpreadsheetColumnID &column_id) const
+{
+ if (component_->instances_amount() == 0) {
+ return {};
+ }
+
+ const int size = this->tot_rows();
+ if (STREQ(column_id.name, "Name")) {
+ Span<int> reference_handles = component_->instance_reference_handles();
+ Span<InstanceReference> references = component_->references();
+ std::unique_ptr<ColumnValues> values = column_values_from_function(
+ "Name", size, [reference_handles, references](int index, CellValue &r_cell_value) {
+ const InstanceReference &reference = references[reference_handles[index]];
+ switch (reference.type()) {
+ case InstanceReference::Type::Object: {
+ Object &object = reference.object();
+ r_cell_value.value_object = ObjectCellValue{&object};
+ break;
+ }
+ case InstanceReference::Type::Collection: {
+ Collection &collection = reference.collection();
+ r_cell_value.value_collection = CollectionCellValue{&collection};
+ break;
+ }
+ case InstanceReference::Type::None: {
+ break;
+ }
+ }
+ });
+ values->default_width = 8.0f;
+ return values;
+ }
+ Span<float4x4> transforms = component_->instance_transforms();
+ if (STREQ(column_id.name, "Position")) {
+ return column_values_from_function(
+ column_id.name,
+ size,
+ [transforms](int index, CellValue &r_cell_value) {
+ r_cell_value.value_float3 = transforms[index].translation();
+ },
+ default_float3_column_width);
+ }
+ if (STREQ(column_id.name, "Rotation")) {
+ return column_values_from_function(
+ column_id.name,
+ size,
+ [transforms](int index, CellValue &r_cell_value) {
+ r_cell_value.value_float3 = transforms[index].to_euler();
+ },
+ default_float3_column_width);
+ }
+ if (STREQ(column_id.name, "Scale")) {
+ return column_values_from_function(
+ column_id.name,
+ size,
+ [transforms](int index, CellValue &r_cell_value) {
+ r_cell_value.value_float3 = transforms[index].scale();
+ },
+ default_float3_column_width);
+ }
+ Span<int> ids = component_->instance_ids();
+ if (STREQ(column_id.name, "ID")) {
+ /* Make the column a bit wider by default, since the IDs tend to be large numbers. */
+ return column_values_from_function(
+ column_id.name,
+ size,
+ [ids](int index, CellValue &r_cell_value) { r_cell_value.value_int = ids[index]; },
+ 5.5f);
+ }
+ return {};
+}
+
+int InstancesDataSource::tot_rows() const
+{
+ return component_->instances_amount();
+}
+
+static GeometrySet get_display_geometry_set(SpaceSpreadsheet *sspreadsheet,
+ Object *object_eval,
+ const GeometryComponentType used_component_type)
+{
+ GeometrySet geometry_set;
+ if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL) {
+ Object *object_orig = DEG_get_original_object(object_eval);
+ if (object_orig->type == OB_MESH) {
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+ if (object_orig->mode == OB_MODE_EDIT) {
+ Mesh *mesh = (Mesh *)object_orig->data;
+ BMEditMesh *em = mesh->edit_mesh;
+ if (em != nullptr) {
+ Mesh *new_mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr);
+ /* This is a potentially heavy operation to do on every redraw. The best solution here is
+ * to display the data directly from the bmesh without a conversion, which can be
+ * implemented a bit later. */
+ BM_mesh_bm_to_me_for_eval(em->bm, new_mesh, nullptr);
+ mesh_component.replace(new_mesh, GeometryOwnershipType::Owned);
+ }
+ }
+ else {
+ Mesh *mesh = (Mesh *)object_orig->data;
+ mesh_component.replace(mesh, GeometryOwnershipType::ReadOnly);
+ }
+ mesh_component.copy_vertex_group_names_from_object(*object_orig);
+ }
+ else if (object_orig->type == OB_POINTCLOUD) {
+ PointCloud *pointcloud = (PointCloud *)object_orig->data;
+ PointCloudComponent &pointcloud_component =
+ geometry_set.get_component_for_write<PointCloudComponent>();
+ pointcloud_component.replace(pointcloud, GeometryOwnershipType::ReadOnly);
+ }
+ }
+ else if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_EVALUATED) {
+ if (used_component_type == GEO_COMPONENT_TYPE_MESH && object_eval->mode == OB_MODE_EDIT) {
+ Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(object_eval, false);
+ if (mesh == nullptr) {
+ return geometry_set;
+ }
+ BKE_mesh_wrapper_ensure_mdata(mesh);
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+ mesh_component.replace(mesh, GeometryOwnershipType::ReadOnly);
+ mesh_component.copy_vertex_group_names_from_object(*object_eval);
+ }
+ else {
+ if (BLI_listbase_count(&sspreadsheet->context_path) == 1) {
+ /* Use final evaluated object. */
+ if (object_eval->runtime.geometry_set_eval != nullptr) {
+ geometry_set = *object_eval->runtime.geometry_set_eval;
+ }
+ }
+ else {
+ if (object_eval->runtime.geometry_set_previews != nullptr) {
+ GHash *ghash = (GHash *)object_eval->runtime.geometry_set_previews;
+ const uint64_t key = ED_spreadsheet_context_path_hash(sspreadsheet);
+ GeometrySet *geometry_set_preview = (GeometrySet *)BLI_ghash_lookup_default(
+ ghash, POINTER_FROM_UINT(key), nullptr);
+ if (geometry_set_preview != nullptr) {
+ geometry_set = *geometry_set_preview;
+ }
+ }
+ }
+ }
+ }
+ return geometry_set;
+}
+
+static GeometryComponentType get_display_component_type(const bContext *C, Object *object_eval)
+{
+ SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
+ if (sspreadsheet->object_eval_state != SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL) {
+ return (GeometryComponentType)sspreadsheet->geometry_component_type;
+ }
+ if (object_eval->type == OB_POINTCLOUD) {
+ return GEO_COMPONENT_TYPE_POINT_CLOUD;
+ }
+ return GEO_COMPONENT_TYPE_MESH;
+}
+
+std::unique_ptr<DataSource> data_source_from_geometry(const bContext *C, Object *object_eval)
+{
+ SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
+ const AttributeDomain domain = (AttributeDomain)sspreadsheet->attribute_domain;
+ const GeometryComponentType component_type = get_display_component_type(C, object_eval);
+ GeometrySet geometry_set = get_display_geometry_set(sspreadsheet, object_eval, component_type);
+
+ if (!geometry_set.has(component_type)) {
+ return {};
+ }
+
+ if (component_type == GEO_COMPONENT_TYPE_INSTANCES) {
+ return std::make_unique<InstancesDataSource>(geometry_set);
+ }
+ return std::make_unique<GeometryDataSource>(object_eval, geometry_set, component_type, domain);
+}
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh
new file mode 100644
index 00000000000..273d39f27bf
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh
@@ -0,0 +1,94 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include <mutex>
+
+#include "BLI_resource_scope.hh"
+
+#include "BKE_geometry_set.hh"
+
+#include "spreadsheet_data_source.hh"
+
+struct bContext;
+
+namespace blender::ed::spreadsheet {
+
+class GeometryDataSource : public DataSource {
+ private:
+ Object *object_eval_;
+ const GeometrySet geometry_set_;
+ const GeometryComponent *component_;
+ AttributeDomain domain_;
+
+ /* Some data is computed on the fly only when it is requested. Computing it does not change the
+ * logical state of this data source. Therefore, the corresponding methods are const and need to
+ * be protected with a mutex. */
+ mutable std::mutex mutex_;
+ mutable ResourceScope scope_;
+
+ public:
+ GeometryDataSource(Object *object_eval,
+ GeometrySet geometry_set,
+ const GeometryComponentType component_type,
+ const AttributeDomain domain)
+ : object_eval_(object_eval),
+ geometry_set_(std::move(geometry_set)),
+ component_(geometry_set_.get_component_for_read(component_type)),
+ domain_(domain)
+ {
+ }
+
+ Object *object_eval() const
+ {
+ return object_eval_;
+ }
+
+ Span<int64_t> get_selected_element_indices() const;
+
+ void foreach_default_column_ids(
+ FunctionRef<void(const SpreadsheetColumnID &)> fn) const override;
+
+ std::unique_ptr<ColumnValues> get_column_values(
+ const SpreadsheetColumnID &column_id) const override;
+
+ int tot_rows() const override;
+};
+
+class InstancesDataSource : public DataSource {
+ const GeometrySet geometry_set_;
+ const InstancesComponent *component_;
+
+ public:
+ InstancesDataSource(GeometrySet geometry_set)
+ : geometry_set_(std::move(geometry_set)),
+ component_(geometry_set_.get_component_for_read<InstancesComponent>())
+ {
+ }
+
+ void foreach_default_column_ids(
+ FunctionRef<void(const SpreadsheetColumnID &)> fn) const override;
+
+ std::unique_ptr<ColumnValues> get_column_values(
+ const SpreadsheetColumnID &column_id) const override;
+
+ int tot_rows() const override;
+};
+
+std::unique_ptr<DataSource> data_source_from_geometry(const bContext *C, Object *object_eval);
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_draw.cc b/source/blender/editors/space_spreadsheet/spreadsheet_draw.cc
index d6379c740e8..b911c80fa63 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_draw.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_draw.cc
@@ -142,7 +142,9 @@ static void draw_left_column_content(const int scroll_offset_y,
ARegion *region,
const SpreadsheetDrawer &drawer)
{
- GPU_scissor_test(true);
+ int old_scissor[4];
+ GPU_scissor_get(old_scissor);
+
GPU_scissor(0, 0, drawer.left_column_width, region->winy - drawer.top_row_height);
uiBlock *left_column_block = UI_block_begin(C, region, __func__, UI_EMBOSS_NONE);
@@ -165,7 +167,7 @@ static void draw_left_column_content(const int scroll_offset_y,
UI_block_end(C, left_column_block);
UI_block_draw(C, left_column_block);
- GPU_scissor_test(false);
+ GPU_scissor(UNPACK4(old_scissor));
}
static void draw_top_row_content(const bContext *C,
@@ -173,7 +175,9 @@ static void draw_top_row_content(const bContext *C,
const SpreadsheetDrawer &drawer,
const int scroll_offset_x)
{
- GPU_scissor_test(true);
+ int old_scissor[4];
+ GPU_scissor_get(old_scissor);
+
GPU_scissor(drawer.left_column_width + 1,
region->winy - drawer.top_row_height,
region->winx - drawer.left_column_width,
@@ -200,7 +204,7 @@ static void draw_top_row_content(const bContext *C,
UI_block_end(C, first_row_block);
UI_block_draw(C, first_row_block);
- GPU_scissor_test(false);
+ GPU_scissor(UNPACK4(old_scissor));
}
static void draw_cell_contents(const bContext *C,
@@ -209,7 +213,9 @@ static void draw_cell_contents(const bContext *C,
const int scroll_offset_x,
const int scroll_offset_y)
{
- GPU_scissor_test(true);
+ int old_scissor[4];
+ GPU_scissor_get(old_scissor);
+
GPU_scissor(drawer.left_column_width + 1,
0,
region->winx - drawer.left_column_width,
@@ -248,7 +254,7 @@ static void draw_cell_contents(const bContext *C,
UI_block_end(C, cells_block);
UI_block_draw(C, cells_block);
- GPU_scissor_test(false);
+ GPU_scissor(UNPACK4(old_scissor));
}
static void update_view2d_tot_rect(const SpreadsheetDrawer &drawer,
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_draw.hh b/source/blender/editors/space_spreadsheet/spreadsheet_draw.hh
index 6828006f4a1..647587ec8b0 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_draw.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_draw.hh
@@ -19,7 +19,6 @@
#include "BLI_vector.hh"
struct uiBlock;
-struct rcti;
struct bContext;
struct ARegion;
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_from_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_from_geometry.cc
deleted file mode 100644
index 910bc0a34ec..00000000000
--- a/source/blender/editors/space_spreadsheet/spreadsheet_from_geometry.cc
+++ /dev/null
@@ -1,447 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#include "BKE_context.h"
-#include "BKE_editmesh.h"
-#include "BKE_lib_id.h"
-#include "BKE_mesh.h"
-#include "BKE_mesh_wrapper.h"
-#include "BKE_modifier.h"
-
-#include "DNA_ID.h"
-#include "DNA_mesh_types.h"
-#include "DNA_meshdata_types.h"
-#include "DNA_space_types.h"
-#include "DNA_userdef_types.h"
-
-#include "DEG_depsgraph_query.h"
-
-#include "bmesh.h"
-
-#include "spreadsheet_from_geometry.hh"
-#include "spreadsheet_intern.hh"
-
-namespace blender::ed::spreadsheet {
-
-using blender::bke::ReadAttribute;
-using blender::bke::ReadAttributePtr;
-
-static void add_columns_for_instances(const InstancesComponent &instances_component,
- SpreadsheetColumnLayout &column_layout,
- ResourceCollector &resources)
-{
- Span<InstancedData> instance_data = instances_component.instanced_data();
- Span<float4x4> transforms = instances_component.transforms();
-
- Vector<std::unique_ptr<SpreadsheetColumn>> &columns =
- resources.construct<Vector<std::unique_ptr<SpreadsheetColumn>>>("columns");
-
- columns.append(spreadsheet_column_from_function(
- "Name", [instance_data](int index, CellValue &r_cell_value) {
- const InstancedData &data = instance_data[index];
- if (data.type == INSTANCE_DATA_TYPE_OBJECT) {
- if (data.data.object != nullptr) {
- r_cell_value.value_object = ObjectCellValue{data.data.object};
- }
- }
- else if (data.type == INSTANCE_DATA_TYPE_COLLECTION) {
- if (data.data.collection != nullptr) {
- r_cell_value.value_collection = CollectionCellValue{data.data.collection};
- }
- }
- }));
-
- columns.last()->default_width = 8.0f;
-
- static std::array<char, 3> axis_char = {'X', 'Y', 'Z'};
- for (const int i : {0, 1, 2}) {
- std::string name = std::string("Position ") + axis_char[i];
- columns.append(spreadsheet_column_from_function(
- name, [transforms, i](int index, CellValue &r_cell_value) {
- r_cell_value.value_float = transforms[index].translation()[i];
- }));
- }
-
- for (const int i : {0, 1, 2}) {
- std::string name = std::string("Rotation ") + axis_char[i];
- columns.append(spreadsheet_column_from_function(
- name, [transforms, i](int index, CellValue &r_cell_value) {
- r_cell_value.value_float = transforms[index].to_euler()[i];
- }));
- }
-
- for (const int i : {0, 1, 2}) {
- std::string name = std::string("Scale ") + axis_char[i];
- columns.append(spreadsheet_column_from_function(
- name, [transforms, i](int index, CellValue &r_cell_value) {
- r_cell_value.value_float = transforms[index].scale()[i];
- }));
- }
-
- for (std::unique_ptr<SpreadsheetColumn> &column : columns) {
- column_layout.columns.append(column.get());
- }
-
- column_layout.row_indices = instance_data.index_range().as_span();
- column_layout.tot_rows = instances_component.instances_amount();
-}
-
-static Vector<std::string> get_sorted_attribute_names_to_display(
- const GeometryComponent &component, const AttributeDomain domain)
-{
- Vector<std::string> attribute_names;
- component.attribute_foreach(
- [&](const StringRef attribute_name, const AttributeMetaData &meta_data) {
- if (meta_data.domain == domain) {
- attribute_names.append(attribute_name);
- }
- return true;
- });
- std::sort(attribute_names.begin(),
- attribute_names.end(),
- [](const std::string &a, const std::string &b) {
- return BLI_strcasecmp_natural(a.c_str(), b.c_str()) < 0;
- });
- return attribute_names;
-}
-
-static void add_columns_for_attribute(const ReadAttribute *attribute,
- const StringRefNull attribute_name,
- Vector<std::unique_ptr<SpreadsheetColumn>> &columns)
-{
- const CustomDataType data_type = attribute->custom_data_type();
- switch (data_type) {
- case CD_PROP_FLOAT: {
- columns.append(spreadsheet_column_from_function(
- attribute_name, [attribute](int index, CellValue &r_cell_value) {
- float value;
- attribute->get(index, &value);
- r_cell_value.value_float = value;
- }));
- break;
- }
- case CD_PROP_FLOAT2: {
- static std::array<char, 2> axis_char = {'X', 'Y'};
- for (const int i : {0, 1}) {
- std::string name = attribute_name + " " + axis_char[i];
- columns.append(spreadsheet_column_from_function(
- name, [attribute, i](int index, CellValue &r_cell_value) {
- float2 value;
- attribute->get(index, &value);
- r_cell_value.value_float = value[i];
- }));
- }
- break;
- }
- case CD_PROP_FLOAT3: {
- static std::array<char, 3> axis_char = {'X', 'Y', 'Z'};
- for (const int i : {0, 1, 2}) {
- std::string name = attribute_name + " " + axis_char[i];
- columns.append(spreadsheet_column_from_function(
- name, [attribute, i](int index, CellValue &r_cell_value) {
- float3 value;
- attribute->get(index, &value);
- r_cell_value.value_float = value[i];
- }));
- }
- break;
- }
- case CD_PROP_COLOR: {
- static std::array<char, 4> axis_char = {'R', 'G', 'B', 'A'};
- for (const int i : {0, 1, 2, 3}) {
- std::string name = attribute_name + " " + axis_char[i];
- columns.append(spreadsheet_column_from_function(
- name, [attribute, i](int index, CellValue &r_cell_value) {
- Color4f value;
- attribute->get(index, &value);
- r_cell_value.value_float = value[i];
- }));
- }
- break;
- }
- case CD_PROP_INT32: {
- columns.append(spreadsheet_column_from_function(
- attribute_name, [attribute](int index, CellValue &r_cell_value) {
- int value;
- attribute->get(index, &value);
- r_cell_value.value_int = value;
- }));
- break;
- }
- case CD_PROP_BOOL: {
- columns.append(spreadsheet_column_from_function(
- attribute_name, [attribute](int index, CellValue &r_cell_value) {
- bool value;
- attribute->get(index, &value);
- r_cell_value.value_bool = value;
- }));
- break;
- }
- default:
- break;
- }
-}
-
-static GeometrySet get_display_geometry_set(SpaceSpreadsheet *sspreadsheet,
- Object *object_eval,
- const GeometryComponentType used_component_type)
-{
- GeometrySet geometry_set;
- if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_FINAL) {
- if (used_component_type == GEO_COMPONENT_TYPE_MESH && object_eval->mode == OB_MODE_EDIT) {
- Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(object_eval, false);
- if (mesh == nullptr) {
- return geometry_set;
- }
- BKE_mesh_wrapper_ensure_mdata(mesh);
- MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
- mesh_component.replace(mesh, GeometryOwnershipType::ReadOnly);
- mesh_component.copy_vertex_group_names_from_object(*object_eval);
- }
- else {
- if (object_eval->runtime.geometry_set_eval != nullptr) {
- /* This does not copy the geometry data itself. */
- geometry_set = *object_eval->runtime.geometry_set_eval;
- }
- }
- }
- else {
- Object *object_orig = DEG_get_original_object(object_eval);
- if (object_orig->type == OB_MESH) {
- MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
- if (object_orig->mode == OB_MODE_EDIT) {
- Mesh *mesh = (Mesh *)object_orig->data;
- BMEditMesh *em = mesh->edit_mesh;
- if (em != nullptr) {
- Mesh *new_mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr);
- /* This is a potentially heavy operation to do on every redraw. The best solution here is
- * to display the data directly from the bmesh without a conversion, which can be
- * implemented a bit later. */
- BM_mesh_bm_to_me_for_eval(em->bm, new_mesh, nullptr);
- mesh_component.replace(new_mesh, GeometryOwnershipType::Owned);
- }
- }
- else {
- Mesh *mesh = (Mesh *)object_orig->data;
- mesh_component.replace(mesh, GeometryOwnershipType::ReadOnly);
- }
- mesh_component.copy_vertex_group_names_from_object(*object_orig);
- }
- else if (object_orig->type == OB_POINTCLOUD) {
- PointCloud *pointcloud = (PointCloud *)object_orig->data;
- PointCloudComponent &pointcloud_component =
- geometry_set.get_component_for_write<PointCloudComponent>();
- pointcloud_component.replace(pointcloud, GeometryOwnershipType::ReadOnly);
- }
- }
- return geometry_set;
-}
-
-using IsVertexSelectedFn = FunctionRef<bool(int vertex_index)>;
-
-static void get_selected_vertex_indices(const Mesh &mesh,
- const IsVertexSelectedFn is_vertex_selected_fn,
- Vector<int64_t> &r_vertex_indices)
-{
- for (const int i : IndexRange(mesh.totvert)) {
- if (is_vertex_selected_fn(i)) {
- r_vertex_indices.append(i);
- }
- }
-}
-
-static void get_selected_corner_indices(const Mesh &mesh,
- const IsVertexSelectedFn is_vertex_selected_fn,
- Vector<int64_t> &r_corner_indices)
-{
- for (const int i : IndexRange(mesh.totloop)) {
- const MLoop &loop = mesh.mloop[i];
- if (is_vertex_selected_fn(loop.v)) {
- r_corner_indices.append(i);
- }
- }
-}
-
-static void get_selected_face_indices(const Mesh &mesh,
- const IsVertexSelectedFn is_vertex_selected_fn,
- Vector<int64_t> &r_face_indices)
-{
- for (const int poly_index : IndexRange(mesh.totpoly)) {
- const MPoly &poly = mesh.mpoly[poly_index];
- bool is_selected = true;
- for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
- const MLoop &loop = mesh.mloop[loop_index];
- if (!is_vertex_selected_fn(loop.v)) {
- is_selected = false;
- break;
- }
- }
- if (is_selected) {
- r_face_indices.append(poly_index);
- }
- }
-}
-
-static void get_selected_edge_indices(const Mesh &mesh,
- const IsVertexSelectedFn is_vertex_selected_fn,
- Vector<int64_t> &r_edge_indices)
-{
- for (const int i : IndexRange(mesh.totedge)) {
- const MEdge &edge = mesh.medge[i];
- if (is_vertex_selected_fn(edge.v1) && is_vertex_selected_fn(edge.v2)) {
- r_edge_indices.append(i);
- }
- }
-}
-
-static void get_selected_indices_on_domain(const Mesh &mesh,
- const AttributeDomain domain,
- const IsVertexSelectedFn is_vertex_selected_fn,
- Vector<int64_t> &r_indices)
-{
- switch (domain) {
- case ATTR_DOMAIN_POINT:
- return get_selected_vertex_indices(mesh, is_vertex_selected_fn, r_indices);
- case ATTR_DOMAIN_FACE:
- return get_selected_face_indices(mesh, is_vertex_selected_fn, r_indices);
- case ATTR_DOMAIN_CORNER:
- return get_selected_corner_indices(mesh, is_vertex_selected_fn, r_indices);
- case ATTR_DOMAIN_EDGE:
- return get_selected_edge_indices(mesh, is_vertex_selected_fn, r_indices);
- default:
- return;
- }
-}
-
-static Span<int64_t> filter_mesh_elements_by_selection(const bContext *C,
- Object *object_eval,
- const MeshComponent *component,
- const AttributeDomain domain,
- ResourceCollector &resources)
-{
- SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
- const bool show_only_selected = sspreadsheet->filter_flag & SPREADSHEET_FILTER_SELECTED_ONLY;
- if (object_eval->mode == OB_MODE_EDIT && show_only_selected) {
- Object *object_orig = DEG_get_original_object(object_eval);
- Vector<int64_t> &visible_rows = resources.construct<Vector<int64_t>>("visible rows");
- const Mesh *mesh_eval = component->get_for_read();
- Mesh *mesh_orig = (Mesh *)object_orig->data;
- BMesh *bm = mesh_orig->edit_mesh->bm;
- BM_mesh_elem_table_ensure(bm, BM_VERT);
-
- int *orig_indices = (int *)CustomData_get_layer(&mesh_eval->vdata, CD_ORIGINDEX);
- if (orig_indices != nullptr) {
- /* Use CD_ORIGINDEX layer if it exists. */
- auto is_vertex_selected = [&](int vertex_index) -> bool {
- const int i_orig = orig_indices[vertex_index];
- if (i_orig < 0) {
- return false;
- }
- if (i_orig >= bm->totvert) {
- return false;
- }
- BMVert *vert = bm->vtable[i_orig];
- return BM_elem_flag_test(vert, BM_ELEM_SELECT);
- };
- get_selected_indices_on_domain(*mesh_eval, domain, is_vertex_selected, visible_rows);
- }
- else if (mesh_eval->totvert == bm->totvert) {
- /* Use a simple heuristic to match original vertices to evaluated ones. */
- auto is_vertex_selected = [&](int vertex_index) -> bool {
- BMVert *vert = bm->vtable[vertex_index];
- return BM_elem_flag_test(vert, BM_ELEM_SELECT);
- };
- get_selected_indices_on_domain(*mesh_eval, domain, is_vertex_selected, visible_rows);
- }
- /* This is safe, because the vector lives in the resource collector. */
- return visible_rows.as_span();
- }
- /* No filter is used. */
- const int domain_size = component->attribute_domain_size(domain);
- return IndexRange(domain_size).as_span();
-}
-
-static GeometryComponentType get_display_component_type(const bContext *C, Object *object_eval)
-{
- SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
- if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_FINAL) {
- return (GeometryComponentType)sspreadsheet->geometry_component_type;
- }
- if (object_eval->type == OB_POINTCLOUD) {
- return GEO_COMPONENT_TYPE_POINT_CLOUD;
- }
- return GEO_COMPONENT_TYPE_MESH;
-}
-
-void spreadsheet_columns_from_geometry(const bContext *C,
- Object *object_eval,
- SpreadsheetColumnLayout &column_layout,
- ResourceCollector &resources)
-{
- SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
- const AttributeDomain domain = (AttributeDomain)sspreadsheet->attribute_domain;
- const GeometryComponentType component_type = get_display_component_type(C, object_eval);
-
- /* Create a resource collector that owns stuff that needs to live until drawing is done. */
- GeometrySet &geometry_set = resources.add_value(
- get_display_geometry_set(sspreadsheet, object_eval, component_type), "geometry set");
-
- const GeometryComponent *component = geometry_set.get_component_for_read(component_type);
- if (component == nullptr) {
- return;
- }
- if (component_type == GEO_COMPONENT_TYPE_INSTANCES) {
- add_columns_for_instances(
- *static_cast<const InstancesComponent *>(component), column_layout, resources);
- return;
- }
-
- if (!component->attribute_domain_supported(domain)) {
- return;
- }
-
- Vector<std::string> attribute_names = get_sorted_attribute_names_to_display(*component, domain);
-
- Vector<std::unique_ptr<SpreadsheetColumn>> &columns =
- resources.construct<Vector<std::unique_ptr<SpreadsheetColumn>>>("columns");
-
- for (StringRefNull attribute_name : attribute_names) {
- ReadAttributePtr attribute_ptr = component->attribute_try_get_for_read(attribute_name);
- ReadAttribute &attribute = *attribute_ptr;
- resources.add(std::move(attribute_ptr), "attribute");
- add_columns_for_attribute(&attribute, attribute_name, columns);
- }
-
- for (std::unique_ptr<SpreadsheetColumn> &column : columns) {
- column_layout.columns.append(column.get());
- }
-
- /* The filter below only works for mesh vertices currently. */
- Span<int64_t> visible_rows;
- if (component_type == GEO_COMPONENT_TYPE_MESH) {
- visible_rows = filter_mesh_elements_by_selection(
- C, object_eval, static_cast<const MeshComponent *>(component), domain, resources);
- }
- else {
- visible_rows = IndexRange(component->attribute_domain_size(domain)).as_span();
- }
-
- const int domain_size = component->attribute_domain_size(domain);
- column_layout.row_indices = visible_rows;
- column_layout.tot_rows = domain_size;
-}
-
-} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc
new file mode 100644
index 00000000000..8079763a339
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc
@@ -0,0 +1,256 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <iomanip>
+#include <sstream>
+
+#include "spreadsheet_layout.hh"
+
+#include "DNA_userdef_types.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "BLF_api.h"
+
+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 &params) const final
+ {
+ const StringRefNull name = spreadsheet_layout_.columns[column_index].values->name();
+ 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 &params) 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.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 &params) const final
+ {
+ const int real_index = spreadsheet_layout_.row_indices[row_index];
+ const ColumnValues &column = *spreadsheet_layout_.columns[column_index].values;
+ CellValue cell_value;
+ column.get_value(real_index, cell_value);
+
+ if (cell_value.value_int.has_value()) {
+ const int value = *cell_value.value_int;
+ const std::string value_str = std::to_string(value);
+ 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);
+ }
+ else if (cell_value.value_float.has_value()) {
+ const float value = *cell_value.value_float;
+ 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.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);
+ }
+ else if (cell_value.value_bool.has_value()) {
+ const bool value = *cell_value.value_bool;
+ 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,
+ 0,
+ 0,
+ nullptr);
+ UI_but_drawflag_disable(but, UI_BUT_ICON_LEFT);
+ }
+ else if (cell_value.value_float2.has_value()) {
+ const float2 value = *cell_value.value_float2;
+ this->draw_float_vector(params, Span(&value.x, 2));
+ }
+ else if (cell_value.value_float3.has_value()) {
+ const float3 value = *cell_value.value_float3;
+ this->draw_float_vector(params, Span(&value.x, 3));
+ }
+ else if (cell_value.value_color.has_value()) {
+ const ColorGeometry4f value = *cell_value.value_color;
+ this->draw_float_vector(params, Span(&value.r, 4));
+ }
+ else if (cell_value.value_object.has_value()) {
+ const ObjectCellValue value = *cell_value.value_object;
+ uiDefIconTextBut(params.block,
+ UI_BTYPE_LABEL,
+ 0,
+ ICON_OBJECT_DATA,
+ reinterpret_cast<const ID *const>(value.object)->name + 2,
+ params.xmin,
+ params.ymin,
+ params.width,
+ params.height,
+ nullptr,
+ 0,
+ 0,
+ 0,
+ 0,
+ nullptr);
+ }
+ else if (cell_value.value_collection.has_value()) {
+ const CollectionCellValue value = *cell_value.value_collection;
+ uiDefIconTextBut(params.block,
+ UI_BTYPE_LABEL,
+ 0,
+ ICON_OUTLINER_COLLECTION,
+ reinterpret_cast<const ID *const>(value.collection)->name + 2,
+ params.xmin,
+ params.ymin,
+ params.width,
+ params.height,
+ nullptr,
+ 0,
+ 0,
+ 0,
+ 0,
+ nullptr);
+ }
+ }
+
+ void draw_float_vector(const CellDrawParams &params, 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.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);
+ }
+ }
+
+ 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
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_layout.hh b/source/blender/editors/space_spreadsheet/spreadsheet_layout.hh
new file mode 100644
index 00000000000..1768af6ae09
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_layout.hh
@@ -0,0 +1,42 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include <optional>
+
+#include "spreadsheet_column_values.hh"
+#include "spreadsheet_draw.hh"
+
+namespace blender::ed::spreadsheet {
+
+/* Layout information for a single column. */
+struct ColumnLayout {
+ const ColumnValues *values;
+ int width;
+};
+
+/* Layout information for the entire spreadsheet. */
+struct SpreadsheetLayout {
+ Vector<ColumnLayout> columns;
+ Span<int64_t> row_indices;
+ int index_column_width = 100;
+};
+
+std::unique_ptr<SpreadsheetDrawer> spreadsheet_drawer_from_layout(
+ const SpreadsheetLayout &spreadsheet_layout);
+
+} // namespace blender::ed::spreadsheet