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.txt14
-rw-r--r--source/blender/editors/space_spreadsheet/space_spreadsheet.cc245
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh2
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_column.cc13
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_column.hh3
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh23
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_context.cc305
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh9
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc168
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh3
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc287
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.hh64
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_dataset_layout.cc112
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_dataset_layout.hh68
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_intern.hh13
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_layout.cc2
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_ops.cc128
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc366
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_row_filter.hh35
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc347
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.hh21
21 files changed, 2079 insertions, 149 deletions
diff --git a/source/blender/editors/space_spreadsheet/CMakeLists.txt b/source/blender/editors/space_spreadsheet/CMakeLists.txt
index b2a0905d4a2..e903feeec1b 100644
--- a/source/blender/editors/space_spreadsheet/CMakeLists.txt
+++ b/source/blender/editors/space_spreadsheet/CMakeLists.txt
@@ -20,12 +20,14 @@ set(INC
../../blenfont
../../blenkernel
../../blenlib
+ ../../blentranslation
../../bmesh
../../depsgraph
../../functions
../../gpu
../../makesdna
../../makesrna
+ ../../nodes
../../windowmanager
../../../../intern/glew-mx
../../../../intern/guardedalloc
@@ -33,23 +35,31 @@ set(INC
set(SRC
space_spreadsheet.cc
- spreadsheet_context.cc
spreadsheet_column.cc
+ spreadsheet_context.cc
spreadsheet_data_source.cc
spreadsheet_data_source_geometry.cc
+ spreadsheet_dataset_draw.cc
+ spreadsheet_dataset_layout.cc
spreadsheet_draw.cc
spreadsheet_layout.cc
spreadsheet_ops.cc
+ spreadsheet_row_filter.cc
+ spreadsheet_row_filter_ui.cc
- spreadsheet_context.hh
spreadsheet_cell_value.hh
spreadsheet_column.hh
spreadsheet_column_values.hh
+ spreadsheet_context.hh
spreadsheet_data_source.hh
spreadsheet_data_source_geometry.hh
+ spreadsheet_dataset_draw.hh
+ spreadsheet_dataset_layout.hh
spreadsheet_draw.hh
spreadsheet_intern.hh
spreadsheet_layout.hh
+ spreadsheet_row_filter.hh
+ spreadsheet_row_filter_ui.hh
)
set(LIB
diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
index 1f0b5d5d13e..fcc92345bea 100644
--- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
+++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
@@ -47,8 +47,11 @@
#include "spreadsheet_context.hh"
#include "spreadsheet_data_source_geometry.hh"
+#include "spreadsheet_dataset_draw.hh"
#include "spreadsheet_intern.hh"
#include "spreadsheet_layout.hh"
+#include "spreadsheet_row_filter.hh"
+#include "spreadsheet_row_filter_ui.hh"
using namespace blender;
using namespace blender::ed::spreadsheet;
@@ -59,6 +62,8 @@ static SpaceLink *spreadsheet_create(const ScrArea *UNUSED(area), const Scene *U
"spreadsheet space");
spreadsheet_space->spacetype = SPACE_SPREADSHEET;
+ spreadsheet_space->filter_flag = SPREADSHEET_FILTER_ENABLE;
+
{
/* Header. */
ARegion *region = (ARegion *)MEM_callocN(sizeof(ARegion), "spreadsheet header");
@@ -76,6 +81,24 @@ static SpaceLink *spreadsheet_create(const ScrArea *UNUSED(area), const Scene *U
}
{
+ /* Dataset Region */
+ ARegion *region = (ARegion *)MEM_callocN(sizeof(ARegion), "spreadsheet dataset region");
+ BLI_addtail(&spreadsheet_space->regionbase, region);
+ region->regiontype = RGN_TYPE_CHANNELS;
+ region->alignment = RGN_ALIGN_LEFT;
+ region->v2d.scroll = (V2D_SCROLL_RIGHT | V2D_SCROLL_BOTTOM);
+ }
+
+ {
+ /* Properties region. */
+ ARegion *region = (ARegion *)MEM_callocN(sizeof(ARegion), "spreadsheet right region");
+ BLI_addtail(&spreadsheet_space->regionbase, region);
+ region->regiontype = RGN_TYPE_UI;
+ region->alignment = RGN_ALIGN_RIGHT;
+ region->flag = RGN_FLAG_HIDDEN;
+ }
+
+ {
/* Main window. */
ARegion *region = (ARegion *)MEM_callocN(sizeof(ARegion), "spreadsheet main region");
BLI_addtail(&spreadsheet_space->regionbase, region);
@@ -88,8 +111,12 @@ static SpaceLink *spreadsheet_create(const ScrArea *UNUSED(area), const Scene *U
static void spreadsheet_free(SpaceLink *sl)
{
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
+
MEM_SAFE_FREE(sspreadsheet->runtime);
+ LISTBASE_FOREACH_MUTABLE (SpreadsheetRowFilter *, row_filter, &sspreadsheet->row_filters) {
+ spreadsheet_row_filter_free(row_filter);
+ }
LISTBASE_FOREACH_MUTABLE (SpreadsheetColumn *, column, &sspreadsheet->columns) {
spreadsheet_column_free(column);
}
@@ -113,6 +140,11 @@ 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->row_filters);
+ LISTBASE_FOREACH (const SpreadsheetRowFilter *, src_filter, &sspreadsheet_old->row_filters) {
+ SpreadsheetRowFilter *new_filter = spreadsheet_row_filter_copy(src_filter);
+ BLI_addtail(&sspreadsheet_new->row_filters, new_filter);
+ }
BLI_listbase_clear(&sspreadsheet_new->columns);
LISTBASE_FOREACH (SpreadsheetColumn *, src_column, &sspreadsheet_old->columns) {
SpreadsheetColumn *new_column = spreadsheet_column_copy(src_column);
@@ -128,8 +160,10 @@ static SpaceLink *spreadsheet_duplicate(SpaceLink *sl)
return (SpaceLink *)sspreadsheet_new;
}
-static void spreadsheet_keymap(wmKeyConfig *UNUSED(keyconf))
+static void spreadsheet_keymap(wmKeyConfig *keyconf)
{
+ /* Entire editor only. */
+ WM_keymap_ensure(keyconf, "Spreadsheet Generic", SPACE_SPREADSHEET, 0);
}
static void spreadsheet_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, ID *old_id, ID *new_id)
@@ -160,11 +194,18 @@ static void spreadsheet_main_region_init(wmWindowManager *wm, ARegion *region)
UI_view2d_region_reinit(&region->v2d, V2D_COMMONVIEW_LIST, region->winx, region->winy);
- wmKeyMap *keymap = WM_keymap_ensure(wm->defaultconf, "View2D Buttons List", 0, 0);
- WM_event_add_keymap_handler(&region->handlers, keymap);
+ {
+ wmKeyMap *keymap = WM_keymap_ensure(wm->defaultconf, "View2D Buttons List", 0, 0);
+ WM_event_add_keymap_handler(&region->handlers, keymap);
+ }
+ {
+ wmKeyMap *keymap = WM_keymap_ensure(
+ wm->defaultconf, "Spreadsheet Generic", SPACE_SPREADSHEET, 0);
+ WM_event_add_keymap_handler(&region->handlers, keymap);
+ }
}
-ID *ED_spreadsheet_get_current_id(struct SpaceSpreadsheet *sspreadsheet)
+ID *ED_spreadsheet_get_current_id(const struct SpaceSpreadsheet *sspreadsheet)
{
if (BLI_listbase_is_empty(&sspreadsheet->context_path)) {
return nullptr;
@@ -181,24 +222,11 @@ ID *ED_spreadsheet_get_current_id(struct SpaceSpreadsheet *sspreadsheet)
static void update_pinned_context_path_if_outdated(const bContext *C)
{
SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
-
- /* 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);
+ Main *bmain = CTX_data_main(C);
+ if (!ED_spreadsheet_context_path_exists(bmain, sspreadsheet)) {
+ ED_spreadsheet_context_path_guess(C, sspreadsheet);
+ if (ED_spreadsheet_context_path_update_tag(sspreadsheet)) {
+ ED_area_tag_redraw(CTX_wm_area(C));
}
}
@@ -211,28 +239,15 @@ static void update_pinned_context_path_if_outdated(const bContext *C)
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);
- 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 (!ED_spreadsheet_context_path_is_active(C, sspreadsheet)) {
+ ED_spreadsheet_context_path_guess(C, sspreadsheet);
+ if (ED_spreadsheet_context_path_update_tag(sspreadsheet)) {
+ ED_area_tag_redraw(CTX_wm_area(C));
}
}
- 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);
- }
}
-static void update_context_path(const bContext *C)
+void spreadsheet_update_context_path(const bContext *C)
{
SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
if (sspreadsheet->flag & SPREADSHEET_FLAG_PINNED) {
@@ -243,28 +258,40 @@ static void update_context_path(const bContext *C)
}
}
-static std::unique_ptr<DataSource> get_data_source(const bContext *C)
+Object *spreadsheet_get_object_eval(const SpaceSpreadsheet *sspreadsheet,
+ const Depsgraph *depsgraph)
{
- Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
- SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
ID *used_id = ED_spreadsheet_get_current_id(sspreadsheet);
if (used_id == nullptr) {
- return {};
+ return nullptr;
}
const ID_Type id_type = GS(used_id->name);
if (id_type != ID_OB) {
- return {};
+ return nullptr;
}
Object *object_orig = (Object *)used_id;
if (!ELEM(object_orig->type, OB_MESH, OB_POINTCLOUD, OB_VOLUME)) {
- return {};
+ return nullptr;
}
+
Object *object_eval = DEG_get_evaluated_object(depsgraph, object_orig);
if (object_eval == nullptr) {
- return {};
+ return nullptr;
}
- return data_source_from_geometry(C, object_eval);
+ return object_eval;
+}
+
+static std::unique_ptr<DataSource> get_data_source(const bContext *C)
+{
+ Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+ SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
+
+ Object *object_eval = spreadsheet_get_object_eval(sspreadsheet, depsgraph);
+ if (object_eval) {
+ return data_source_from_geometry(C, object_eval);
+ }
+ return {};
}
static float get_column_width(const ColumnValues &values)
@@ -327,7 +354,7 @@ static void update_visible_columns(ListBase &columns, DataSource &data_source)
static void spreadsheet_main_region_draw(const bContext *C, ARegion *region)
{
SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
- update_context_path(C);
+ spreadsheet_update_context_path(C);
std::unique_ptr<DataSource> data_source = get_data_source(C);
if (!data_source) {
@@ -346,24 +373,14 @@ static void spreadsheet_main_region_draw(const bContext *C, ARegion *region)
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});
+
+ spreadsheet_column_assign_runtime_data(column, values->type(), values->name());
}
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();
- }
- }
- }
- }
+ spreadsheet_layout.row_indices = spreadsheet_filter_rows(
+ *sspreadsheet, spreadsheet_layout, *data_source, scope);
sspreadsheet->runtime->tot_columns = spreadsheet_layout.columns.size();
sspreadsheet->runtime->tot_rows = tot_rows;
@@ -372,9 +389,11 @@ static void spreadsheet_main_region_draw(const bContext *C, ARegion *region)
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. */
+ /* Tag other regions for redraw, because the main region updates data for them. */
ARegion *footer = BKE_area_find_region_type(CTX_wm_area(C), RGN_TYPE_FOOTER);
ED_region_tag_redraw(footer);
+ ARegion *sidebar = BKE_area_find_region_type(CTX_wm_area(C), RGN_TYPE_UI);
+ ED_region_tag_redraw(sidebar);
}
static void spreadsheet_main_region_listener(const wmRegionListenerParams *params)
@@ -419,7 +438,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);
+ spreadsheet_update_context_path(C);
ED_region_header(C, region);
}
@@ -511,6 +530,77 @@ static void spreadsheet_footer_region_listener(const wmRegionListenerParams *UNU
{
}
+static void spreadsheet_dataset_region_listener(const wmRegionListenerParams *params)
+{
+ ARegion *region = params->region;
+ wmNotifier *wmn = params->notifier;
+
+ switch (wmn->category) {
+ case NC_SCENE: {
+ switch (wmn->data) {
+ case ND_FRAME:
+ ED_region_tag_redraw(region);
+ break;
+ }
+ break;
+ }
+ case NC_TEXTURE:
+ ED_region_tag_redraw(region);
+ break;
+ }
+
+ spreadsheet_header_region_listener(params);
+}
+
+static void spreadsheet_dataset_region_init(wmWindowManager *wm, ARegion *region)
+{
+ region->v2d.scroll |= V2D_SCROLL_RIGHT;
+ region->v2d.scroll &= ~(V2D_SCROLL_LEFT | V2D_SCROLL_TOP | V2D_SCROLL_BOTTOM);
+ region->v2d.scroll |= V2D_SCROLL_HORIZONTAL_HIDE;
+ region->v2d.scroll |= V2D_SCROLL_VERTICAL_HIDE;
+
+ UI_view2d_region_reinit(&region->v2d, V2D_COMMONVIEW_LIST, region->winx, region->winy);
+
+ wmKeyMap *keymap = WM_keymap_ensure(
+ wm->defaultconf, "Spreadsheet Generic", SPACE_SPREADSHEET, 0);
+ WM_event_add_keymap_handler(&region->handlers, keymap);
+}
+
+static void spreadsheet_dataset_region_draw(const bContext *C, ARegion *region)
+{
+ spreadsheet_update_context_path(C);
+
+ View2D *v2d = &region->v2d;
+ UI_view2d_view_ortho(v2d);
+ UI_ThemeClearColor(TH_BACK);
+
+ draw_dataset_in_region(C, region);
+
+ /* reset view matrix */
+ UI_view2d_view_restore(C);
+
+ /* scrollers */
+ UI_view2d_scrollers_draw(v2d, nullptr);
+}
+
+static void spreadsheet_sidebar_init(wmWindowManager *wm, ARegion *region)
+{
+ UI_panel_category_active_set_default(region, "Filters");
+ ED_region_panels_init(wm, region);
+
+ wmKeyMap *keymap = WM_keymap_ensure(
+ wm->defaultconf, "Spreadsheet Generic", SPACE_SPREADSHEET, 0);
+ WM_event_add_keymap_handler(&region->handlers, keymap);
+}
+
+static void spreadsheet_right_region_free(ARegion *UNUSED(region))
+{
+}
+
+static void spreadsheet_right_region_listener(const wmRegionListenerParams *UNUSED(params))
+{
+}
+
void ED_spacetype_spreadsheet(void)
{
SpaceType *st = (SpaceType *)MEM_callocN(sizeof(SpaceType), "spacetype spreadsheet");
@@ -563,5 +653,30 @@ void ED_spacetype_spreadsheet(void)
art->listener = spreadsheet_footer_region_listener;
BLI_addhead(&st->regiontypes, art);
+ /* regions: right panel buttons */
+ art = (ARegionType *)MEM_callocN(sizeof(ARegionType), "spacetype spreadsheet right region");
+ art->regionid = RGN_TYPE_UI;
+ art->prefsizex = UI_SIDEBAR_PANEL_WIDTH;
+ art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_FRAMES;
+
+ art->init = spreadsheet_sidebar_init;
+ art->layout = ED_region_panels_layout;
+ art->draw = ED_region_panels_draw;
+ art->free = spreadsheet_right_region_free;
+ art->listener = spreadsheet_right_region_listener;
+ BLI_addhead(&st->regiontypes, art);
+
+ register_row_filter_panels(*art);
+
+ /* regions: channels */
+ art = (ARegionType *)MEM_callocN(sizeof(ARegionType), "spreadsheet dataset region");
+ art->regionid = RGN_TYPE_CHANNELS;
+ art->prefsizex = 200 + V2D_SCROLL_WIDTH;
+ art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D;
+ art->init = spreadsheet_dataset_region_init;
+ art->draw = spreadsheet_dataset_region_draw;
+ art->listener = spreadsheet_dataset_region_listener;
+ BLI_addhead(&st->regiontypes, art);
+
BKE_spacetype_register(st);
}
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh
index d1e80f1d87e..c9b73aabf96 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh
@@ -50,7 +50,7 @@ class CellValue {
std::optional<bool> value_bool;
std::optional<float2> value_float2;
std::optional<float3> value_float3;
- std::optional<Color4f> value_color;
+ std::optional<ColorGeometry4f> value_color;
std::optional<ObjectCellValue> value_object;
std::optional<CollectionCellValue> value_collection;
};
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column.cc b/source/blender/editors/space_spreadsheet/spreadsheet_column.cc
index de40545fdae..ee08c86b29f 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_column.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_column.cc
@@ -56,16 +56,29 @@ SpreadsheetColumn *spreadsheet_column_new(SpreadsheetColumnID *column_id)
return column;
}
+void spreadsheet_column_assign_runtime_data(SpreadsheetColumn *column,
+ const eSpreadsheetColumnValueType data_type,
+ const StringRefNull display_name)
+{
+ column->data_type = data_type;
+ MEM_SAFE_FREE(column->display_name);
+ column->display_name = BLI_strdup(display_name.c_str());
+}
+
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);
+ if (src_column->display_name != nullptr) {
+ new_column->display_name = BLI_strdup(src_column->display_name);
+ }
return new_column;
}
void spreadsheet_column_free(SpreadsheetColumn *column)
{
spreadsheet_column_id_free(column->id);
+ MEM_SAFE_FREE(column->display_name);
MEM_freeN(column);
}
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column.hh b/source/blender/editors/space_spreadsheet/spreadsheet_column.hh
index bb245851d55..1a03278acad 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_column.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_column.hh
@@ -43,6 +43,9 @@ 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_assign_runtime_data(SpreadsheetColumn *column,
+ const eSpreadsheetColumnValueType data_type,
+ const StringRefNull display_name);
void spreadsheet_column_free(SpreadsheetColumn *column);
} // 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
index 373c988a41c..68370cf6a44 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh
@@ -16,6 +16,8 @@
#pragma once
+#include "DNA_space_types.h"
+
#include "BLI_string_ref.hh"
#include "spreadsheet_cell_value.hh"
@@ -28,11 +30,13 @@ namespace blender::ed::spreadsheet {
*/
class ColumnValues {
protected:
+ eSpreadsheetColumnValueType type_;
std::string name_;
int size_;
public:
- ColumnValues(std::string name, const int size) : name_(std::move(name)), size_(size)
+ ColumnValues(const eSpreadsheetColumnValueType type, std::string name, const int size)
+ : type_(type), name_(std::move(name)), size_(size)
{
}
@@ -40,6 +44,11 @@ class ColumnValues {
virtual void get_value(int index, CellValue &r_cell_value) const = 0;
+ eSpreadsheetColumnValueType type() const
+ {
+ return type_;
+ }
+
StringRefNull name() const
{
return name_;
@@ -60,8 +69,11 @@ template<typename GetValueF> class LambdaColumnValues : public ColumnValues {
GetValueF get_value_;
public:
- LambdaColumnValues(std::string name, int size, GetValueF get_value)
- : ColumnValues(std::move(name), size), get_value_(std::move(get_value))
+ LambdaColumnValues(const eSpreadsheetColumnValueType type,
+ std::string name,
+ int size,
+ GetValueF get_value)
+ : ColumnValues(type, std::move(name), size), get_value_(std::move(get_value))
{
}
@@ -73,13 +85,14 @@ template<typename GetValueF> class LambdaColumnValues : public ColumnValues {
/* 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,
+std::unique_ptr<ColumnValues> column_values_from_function(const eSpreadsheetColumnValueType type,
+ 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));
+ type, std::move(name), size, std::move(get_value));
column_values->default_width = default_width;
return column_values;
}
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_context.cc b/source/blender/editors/space_spreadsheet/spreadsheet_context.cc
index 3eb43338908..1ac2075e281 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_context.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_context.cc
@@ -24,16 +24,28 @@
#include "BLI_utildefines.h"
#include "BLI_vector.hh"
+#include "ED_screen.h"
#include "ED_spreadsheet.h"
#include "DEG_depsgraph.h"
+#include "BKE_context.h"
#include "BKE_main.h"
#include "BKE_modifier.h"
+#include "BKE_node.h"
#include "BKE_object.h"
+#include "BKE_workspace.h"
+
+#include "DNA_modifier_types.h"
+#include "DNA_windowmanager_types.h"
#include "spreadsheet_context.hh"
+using blender::IndexRange;
+using blender::Span;
+using blender::StringRef;
+using blender::Vector;
+
namespace blender::ed::spreadsheet {
static SpreadsheetContextObject *spreadsheet_context_object_new()
@@ -206,28 +218,30 @@ void spreadsheet_context_free(SpreadsheetContext *context)
/**
* 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.
+ * \return True when any data has been tagged for update.
*/
-static void spreadsheet_context_update_tag(SpaceSpreadsheet *sspreadsheet)
+static bool spreadsheet_context_update_tag(SpaceSpreadsheet *sspreadsheet)
{
using namespace blender;
Vector<const SpreadsheetContext *> context_path = sspreadsheet->context_path;
if (context_path.is_empty()) {
- return;
+ return false;
}
if (context_path[0]->type != SPREADSHEET_CONTEXT_OBJECT) {
- return;
+ return false;
}
SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context_path[0];
Object *object = object_context->object;
if (object == nullptr) {
- return;
+ return false;
}
if (context_path.size() == 1) {
/* No need to reevaluate, when the final or original object is viewed. */
- return;
+ return false;
}
DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY);
+ return true;
}
} // namespace blender::ed::spreadsheet
@@ -250,30 +264,47 @@ void ED_spreadsheet_context_path_clear(struct SpaceSpreadsheet *sspreadsheet)
BLI_listbase_clear(&sspreadsheet->context_path);
}
-void ED_spreadsheet_context_path_update_tag(SpaceSpreadsheet *sspreadsheet)
+bool ED_spreadsheet_context_path_update_tag(SpaceSpreadsheet *sspreadsheet)
{
- blender::ed::spreadsheet::spreadsheet_context_update_tag(sspreadsheet);
+ return blender::ed::spreadsheet::spreadsheet_context_update_tag(sspreadsheet);
}
-uint64_t ED_spreadsheet_context_path_hash(SpaceSpreadsheet *sspreadsheet)
+uint64_t ED_spreadsheet_context_path_hash(const SpaceSpreadsheet *sspreadsheet)
{
BLI_HashMurmur2A mm2;
BLI_hash_mm2a_init(&mm2, 1234);
- LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) {
+ LISTBASE_FOREACH (const 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)
+void ED_spreadsheet_context_path_set_geometry_node(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;
+ /* Try to find the modifier the node tree belongs to. */
ModifierData *modifier = BKE_object_active_modifier(object);
+ if (modifier && modifier->type != eModifierType_Nodes) {
+ modifier = nullptr;
+ LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
+ if (md->type == eModifierType_Nodes) {
+ NodesModifierData *nmd = (NodesModifierData *)md;
+ if (nmd->node_group == snode->nodetree) {
+ modifier = md;
+ break;
+ }
+ }
+ }
+ }
+ if (modifier == nullptr) {
+ return;
+ }
+
+ ED_spreadsheet_context_path_clear(sspreadsheet);
{
SpreadsheetContextObject *context = spreadsheet_context_object_new();
@@ -302,5 +333,251 @@ void ED_spreadsheet_set_geometry_node_context(struct SpaceSpreadsheet *sspreadsh
BLI_addtail(&sspreadsheet->context_path, context);
}
- sspreadsheet->object_eval_state = SPREADSHEET_OBJECT_EVAL_STATE_EVALUATED;
+ sspreadsheet->object_eval_state = SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE;
+}
+
+void ED_spreadsheet_context_paths_set_geometry_node(Main *bmain, SpaceNode *snode, bNode *node)
+{
+ wmWindowManager *wm = (wmWindowManager *)bmain->wm.first;
+ if (wm == nullptr) {
+ return;
+ }
+ LISTBASE_FOREACH (wmWindow *, window, &wm->windows) {
+ bScreen *screen = BKE_workspace_active_screen_get(window->workspace_hook);
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ SpaceLink *sl = (SpaceLink *)area->spacedata.first;
+ if (sl->spacetype == SPACE_SPREADSHEET) {
+ SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
+ if ((sspreadsheet->flag & SPREADSHEET_FLAG_PINNED) == 0) {
+ const uint64_t context_hash_before = ED_spreadsheet_context_path_hash(sspreadsheet);
+ ED_spreadsheet_context_path_set_geometry_node(sspreadsheet, snode, node);
+ const uint64_t context_hash_after = ED_spreadsheet_context_path_hash(sspreadsheet);
+ if (context_hash_before != context_hash_after) {
+ ED_spreadsheet_context_path_update_tag(sspreadsheet);
+ }
+ ED_area_tag_redraw(area);
+ }
+ }
+ }
+ }
+}
+
+void ED_spreadsheet_context_path_set_evaluated_object(SpaceSpreadsheet *sspreadsheet,
+ Object *object)
+{
+ using namespace blender::ed::spreadsheet;
+ ED_spreadsheet_context_path_clear(sspreadsheet);
+
+ SpreadsheetContextObject *context = spreadsheet_context_object_new();
+ context->object = object;
+ BLI_addtail(&sspreadsheet->context_path, context);
+}
+
+void ED_spreadsheet_context_path_guess(const bContext *C, SpaceSpreadsheet *sspreadsheet)
+{
+ ED_spreadsheet_context_path_clear(sspreadsheet);
+
+ Main *bmain = CTX_data_main(C);
+ wmWindowManager *wm = (wmWindowManager *)bmain->wm.first;
+ if (wm == nullptr) {
+ return;
+ }
+
+ if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE) {
+ LISTBASE_FOREACH (wmWindow *, window, &wm->windows) {
+ bScreen *screen = BKE_workspace_active_screen_get(window->workspace_hook);
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ SpaceLink *sl = (SpaceLink *)area->spacedata.first;
+ if (sl->spacetype == SPACE_NODE) {
+ SpaceNode *snode = (SpaceNode *)sl;
+ if (snode->edittree != nullptr) {
+ if (snode->edittree->type == NTREE_GEOMETRY) {
+ LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
+ if (node->type == GEO_NODE_VIEWER) {
+ if (node->flag & NODE_DO_OUTPUT) {
+ ED_spreadsheet_context_path_set_geometry_node(sspreadsheet, snode, node);
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Object *active_object = CTX_data_active_object(C);
+ if (active_object != nullptr) {
+ ED_spreadsheet_context_path_set_evaluated_object(sspreadsheet, active_object);
+ return;
+ }
+}
+
+bool ED_spreadsheet_context_path_is_active(const bContext *C, SpaceSpreadsheet *sspreadsheet)
+{
+ Main *bmain = CTX_data_main(C);
+ wmWindowManager *wm = (wmWindowManager *)bmain->wm.first;
+ if (wm == nullptr) {
+ return false;
+ }
+ Vector<SpreadsheetContext *> context_path = sspreadsheet->context_path;
+ if (context_path.is_empty()) {
+ return false;
+ }
+ if (context_path[0]->type != SPREADSHEET_CONTEXT_OBJECT) {
+ return false;
+ }
+ Object *object = ((SpreadsheetContextObject *)context_path[0])->object;
+ if (object == nullptr) {
+ return false;
+ }
+ if (context_path.size() == 1) {
+ if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE) {
+ return false;
+ }
+ Object *active_object = CTX_data_active_object(C);
+ return object == active_object;
+ }
+ if (sspreadsheet->object_eval_state != SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE) {
+ return false;
+ }
+ if (context_path[1]->type != SPREADSHEET_CONTEXT_MODIFIER) {
+ return false;
+ }
+ const char *modifier_name = ((SpreadsheetContextModifier *)context_path[1])->modifier_name;
+ const ModifierData *modifier = BKE_modifiers_findby_name(object, modifier_name);
+ if (modifier == nullptr) {
+ return false;
+ }
+ if (!(modifier->flag & eModifierFlag_Active)) {
+ return false;
+ }
+ if (modifier->type != eModifierType_Nodes) {
+ return false;
+ }
+ bNodeTree *root_node_tree = ((NodesModifierData *)modifier)->node_group;
+ if (root_node_tree == nullptr) {
+ return false;
+ }
+ const Span<SpreadsheetContext *> node_context_path = context_path.as_span().drop_front(2);
+ if (node_context_path.is_empty()) {
+ return false;
+ }
+
+ LISTBASE_FOREACH (wmWindow *, window, &wm->windows) {
+ bScreen *screen = BKE_workspace_active_screen_get(window->workspace_hook);
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ SpaceLink *sl = (SpaceLink *)area->spacedata.first;
+ if (sl->spacetype != SPACE_NODE) {
+ continue;
+ }
+ SpaceNode *snode = (SpaceNode *)sl;
+ if (snode->nodetree != root_node_tree) {
+ continue;
+ }
+ if (snode->id != &object->id) {
+ continue;
+ }
+ Vector<bNodeTreePath *> tree_path = snode->treepath;
+ if (node_context_path.size() != tree_path.size()) {
+ continue;
+ }
+ int valid_count = 0;
+ for (const int i : IndexRange(tree_path.size() - 1)) {
+ if (node_context_path[i]->type != SPREADSHEET_CONTEXT_NODE) {
+ break;
+ }
+ SpreadsheetContextNode *node_context = (SpreadsheetContextNode *)node_context_path[i];
+ if (!STREQ(node_context->node_name, tree_path[i]->node_name)) {
+ break;
+ }
+ valid_count++;
+ }
+ if (valid_count != tree_path.size() - 1) {
+ continue;
+ }
+ SpreadsheetContext *last_context = node_context_path.last();
+ if (last_context->type != SPREADSHEET_CONTEXT_NODE) {
+ return false;
+ }
+ const char *node_name = ((SpreadsheetContextNode *)last_context)->node_name;
+ bNode *node = nodeFindNodebyName(snode->edittree, node_name);
+ if (node == nullptr) {
+ return false;
+ }
+ if (node->type != GEO_NODE_VIEWER) {
+ return false;
+ }
+ if (!(node->flag & NODE_DO_OUTPUT)) {
+ return false;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ED_spreadsheet_context_path_exists(Main *UNUSED(bmain), SpaceSpreadsheet *sspreadsheet)
+{
+ Vector<SpreadsheetContext *> context_path = sspreadsheet->context_path;
+ if (context_path.is_empty()) {
+ return false;
+ }
+ if (context_path[0]->type != SPREADSHEET_CONTEXT_OBJECT) {
+ return false;
+ }
+ Object *object = ((SpreadsheetContextObject *)context_path[0])->object;
+ if (object == nullptr) {
+ return false;
+ }
+ if (context_path.size() == 1) {
+ return true;
+ }
+ if (context_path[1]->type != SPREADSHEET_CONTEXT_MODIFIER) {
+ return false;
+ }
+ const char *modifier_name = ((SpreadsheetContextModifier *)context_path[1])->modifier_name;
+ const ModifierData *modifier = BKE_modifiers_findby_name(object, modifier_name);
+ if (modifier == nullptr) {
+ return false;
+ }
+ if (modifier->type != eModifierType_Nodes) {
+ return false;
+ }
+ bNodeTree *root_node_tree = ((NodesModifierData *)modifier)->node_group;
+ if (root_node_tree == nullptr) {
+ return false;
+ }
+ const Span<SpreadsheetContext *> node_context_path = context_path.as_span().drop_front(2);
+ if (node_context_path.is_empty()) {
+ return false;
+ }
+ bNodeTree *node_tree = root_node_tree;
+ for (const int i : node_context_path.index_range()) {
+ if (node_context_path[i]->type != SPREADSHEET_CONTEXT_NODE) {
+ return false;
+ }
+ const char *node_name = ((SpreadsheetContextNode *)node_context_path[i])->node_name;
+ bNode *node = nodeFindNodebyName(node_tree, node_name);
+ if (node == nullptr) {
+ return false;
+ }
+ if (node->type == GEO_NODE_VIEWER) {
+ if (i == node_context_path.index_range().last()) {
+ return true;
+ }
+ return false;
+ }
+ if (node->id != nullptr) {
+ if (GS(node->id->name) != ID_NT) {
+ return false;
+ }
+ node_tree = (bNodeTree *)node->id;
+ }
+ else {
+ return false;
+ }
+ }
+ return false;
}
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh b/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh
index de47109a144..2ea7fb5809f 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh
@@ -54,6 +54,15 @@ class DataSource {
}
/**
+ * Returns true if the data source has the ability to limit visible rows
+ * by user interface selection status.
+ */
+ virtual bool has_selection_filter() const
+ {
+ return false;
+ }
+
+ /**
* Returns the number of rows in columns returned by #get_column_values.
*/
virtual int tot_rows() const
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
index 02ffa1259fc..e38c70afd0f 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
@@ -31,11 +31,15 @@
#include "ED_spreadsheet.h"
+#include "NOD_geometry_nodes_eval_log.hh"
+
#include "bmesh.h"
#include "spreadsheet_data_source_geometry.hh"
#include "spreadsheet_intern.hh"
+namespace geo_log = blender::nodes::geometry_nodes_eval_log;
+
namespace blender::ed::spreadsheet {
void GeometryDataSource::foreach_default_column_ids(
@@ -69,28 +73,35 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
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;
- });
+ return column_values_from_function(SPREADSHEET_VALUE_TYPE_FLOAT,
+ 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;
- });
+ return column_values_from_function(SPREADSHEET_VALUE_TYPE_INT32,
+ 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;
- });
+ return column_values_from_function(SPREADSHEET_VALUE_TYPE_BOOL,
+ 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(
+ SPREADSHEET_VALUE_TYPE_FLOAT2,
column_id.name,
domain_size,
[varray](int index, CellValue &r_cell_value) {
@@ -102,6 +113,7 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
}
case CD_PROP_FLOAT3: {
return column_values_from_function(
+ SPREADSHEET_VALUE_TYPE_FLOAT3,
column_id.name,
domain_size,
[varray](int index, CellValue &r_cell_value) {
@@ -113,10 +125,11 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
}
case CD_PROP_COLOR: {
return column_values_from_function(
+ SPREADSHEET_VALUE_TYPE_COLOR,
column_id.name,
domain_size,
[varray](int index, CellValue &r_cell_value) {
- Color4f value;
+ ColorGeometry4f value;
varray->get(index, &value);
r_cell_value.value_color = value;
},
@@ -137,55 +150,63 @@ 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)
+ MutableSpan<bool> selection)
{
for (const int i : IndexRange(mesh.totvert)) {
- if (is_vertex_selected_fn(i)) {
- r_vertex_indices.append(i);
+ if (!selection[i]) {
+ continue;
+ }
+ if (!is_vertex_selected_fn(i)) {
+ selection[i] = false;
}
}
}
static void get_selected_corner_indices(const Mesh &mesh,
const IsVertexSelectedFn is_vertex_selected_fn,
- Vector<int64_t> &r_corner_indices)
+ MutableSpan<bool> selection)
{
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);
+ if (!selection[i]) {
+ continue;
+ }
+ if (!is_vertex_selected_fn(loop.v)) {
+ selection[i] = false;
}
}
}
static void get_selected_face_indices(const Mesh &mesh,
const IsVertexSelectedFn is_vertex_selected_fn,
- Vector<int64_t> &r_face_indices)
+ MutableSpan<bool> selection)
{
for (const int poly_index : IndexRange(mesh.totpoly)) {
+ if (!selection[poly_index]) {
+ continue;
+ }
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;
+ selection[poly_index] = 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)
+ MutableSpan<bool> selection)
{
for (const int i : IndexRange(mesh.totedge)) {
+ if (!selection[i]) {
+ continue;
+ }
const MEdge &edge = mesh.medge[i];
- if (is_vertex_selected_fn(edge.v1) && is_vertex_selected_fn(edge.v2)) {
- r_edge_indices.append(i);
+ if (!is_vertex_selected_fn(edge.v1) || !is_vertex_selected_fn(edge.v2)) {
+ selection[i] = false;
}
}
}
@@ -193,30 +214,48 @@ static void get_selected_edge_indices(const Mesh &mesh,
static void get_selected_indices_on_domain(const Mesh &mesh,
const AttributeDomain domain,
const IsVertexSelectedFn is_vertex_selected_fn,
- Vector<int64_t> &r_indices)
+ MutableSpan<bool> selection)
{
switch (domain) {
case ATTR_DOMAIN_POINT:
- return get_selected_vertex_indices(mesh, is_vertex_selected_fn, r_indices);
+ return get_selected_vertex_indices(mesh, is_vertex_selected_fn, selection);
case ATTR_DOMAIN_FACE:
- return get_selected_face_indices(mesh, is_vertex_selected_fn, r_indices);
+ return get_selected_face_indices(mesh, is_vertex_selected_fn, selection);
case ATTR_DOMAIN_CORNER:
- return get_selected_corner_indices(mesh, is_vertex_selected_fn, r_indices);
+ return get_selected_corner_indices(mesh, is_vertex_selected_fn, selection);
case ATTR_DOMAIN_EDGE:
- return get_selected_edge_indices(mesh, is_vertex_selected_fn, r_indices);
+ return get_selected_edge_indices(mesh, is_vertex_selected_fn, selection);
default:
return;
}
}
-Span<int64_t> GeometryDataSource::get_selected_element_indices() const
+/**
+ * Only data sets corresponding to mesh objects in edit mode currently support selection filtering.
+ */
+bool GeometryDataSource::has_selection_filter() const
+{
+ Object *object_orig = DEG_get_original_object(object_eval_);
+ if (object_orig->type != OB_MESH) {
+ return false;
+ }
+ if (object_orig->mode != OB_MODE_EDIT) {
+ return false;
+ }
+ if (component_->type() != GEO_COMPONENT_TYPE_MESH) {
+ return false;
+ }
+
+ return true;
+}
+
+void GeometryDataSource::apply_selection_filter(MutableSpan<bool> rows_included) 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;
@@ -237,7 +276,7 @@ Span<int64_t> GeometryDataSource::get_selected_element_indices() const
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);
+ get_selected_indices_on_domain(*mesh_eval, domain_, is_vertex_selected, rows_included);
}
else if (mesh_eval->totvert == bm->totvert) {
/* Use a simple heuristic to match original vertices to evaluated ones. */
@@ -245,10 +284,8 @@ Span<int64_t> GeometryDataSource::get_selected_element_indices() const
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);
+ get_selected_indices_on_domain(*mesh_eval, domain_, is_vertex_selected, rows_included);
}
-
- return indices;
}
void InstancesDataSource::foreach_default_column_ids(
@@ -279,7 +316,10 @@ std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values(
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) {
+ SPREADSHEET_VALUE_TYPE_INSTANCES,
+ "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: {
@@ -303,6 +343,7 @@ std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values(
Span<float4x4> transforms = component_->instance_transforms();
if (STREQ(column_id.name, "Position")) {
return column_values_from_function(
+ SPREADSHEET_VALUE_TYPE_FLOAT3,
column_id.name,
size,
[transforms](int index, CellValue &r_cell_value) {
@@ -312,6 +353,7 @@ std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values(
}
if (STREQ(column_id.name, "Rotation")) {
return column_values_from_function(
+ SPREADSHEET_VALUE_TYPE_FLOAT3,
column_id.name,
size,
[transforms](int index, CellValue &r_cell_value) {
@@ -321,6 +363,7 @@ std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values(
}
if (STREQ(column_id.name, "Scale")) {
return column_values_from_function(
+ SPREADSHEET_VALUE_TYPE_FLOAT3,
column_id.name,
size,
[transforms](int index, CellValue &r_cell_value) {
@@ -332,6 +375,7 @@ std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values(
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(
+ SPREADSHEET_VALUE_TYPE_INT32,
column_id.name,
size,
[ids](int index, CellValue &r_cell_value) { r_cell_value.value_int = ids[index]; },
@@ -345,9 +389,9 @@ 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 spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *sspreadsheet,
+ Object *object_eval,
+ const GeometryComponentType used_component_type)
{
GeometrySet geometry_set;
if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL) {
@@ -370,7 +414,6 @@ static GeometrySet get_display_geometry_set(SpaceSpreadsheet *sspreadsheet,
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;
@@ -379,7 +422,7 @@ static GeometrySet get_display_geometry_set(SpaceSpreadsheet *sspreadsheet,
pointcloud_component.replace(pointcloud, GeometryOwnershipType::ReadOnly);
}
}
- else if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_EVALUATED) {
+ else {
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) {
@@ -388,7 +431,6 @@ static GeometrySet get_display_geometry_set(SpaceSpreadsheet *sspreadsheet,
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) {
@@ -398,13 +440,18 @@ static GeometrySet get_display_geometry_set(SpaceSpreadsheet *sspreadsheet,
}
}
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;
+ const geo_log::NodeLog *node_log =
+ geo_log::ModifierLog::find_node_by_spreadsheet_editor_context(*sspreadsheet);
+ if (node_log != nullptr) {
+ for (const geo_log::SocketLog &input_log : node_log->input_logs()) {
+ if (const geo_log::GeometryValueLog *geo_value_log =
+ dynamic_cast<const geo_log::GeometryValueLog *>(input_log.value())) {
+ const GeometrySet *full_geometry = geo_value_log->full_geometry();
+ if (full_geometry != nullptr) {
+ geometry_set = *full_geometry;
+ break;
+ }
+ }
}
}
}
@@ -430,7 +477,8 @@ std::unique_ptr<DataSource> data_source_from_geometry(const bContext *C, Object
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);
+ GeometrySet geometry_set = spreadsheet_get_display_geometry_set(
+ sspreadsheet, object_eval, component_type);
if (!geometry_set.has(component_type)) {
return {};
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh
index 273d39f27bf..d1b5dc6845e 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh
@@ -58,7 +58,8 @@ class GeometryDataSource : public DataSource {
return object_eval_;
}
- Span<int64_t> get_selected_element_indices() const;
+ bool has_selection_filter() const override;
+ void apply_selection_filter(MutableSpan<bool> rows_included) const;
void foreach_default_column_ids(
FunctionRef<void(const SpreadsheetColumnID &)> fn) const override;
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc
new file mode 100644
index 00000000000..2b31ce7d03c
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc
@@ -0,0 +1,287 @@
+/*
+ * 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 <array>
+
+#include "DNA_space_types.h"
+#include "DNA_windowmanager_types.h"
+
+#include "BKE_context.h"
+
+#include "BLF_api.h"
+
+#include "BLI_rect.h"
+
+#include "RNA_access.h"
+
+#include "UI_interface.h"
+#include "UI_view2d.h"
+
+#include "WM_types.h"
+
+#include "spreadsheet_dataset_draw.hh"
+#include "spreadsheet_draw.hh"
+#include "spreadsheet_intern.hh"
+
+static int is_component_row_selected(struct uiBut *but, const void *arg)
+{
+ SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)arg;
+
+ GeometryComponentType component = (GeometryComponentType)UI_but_datasetrow_component_get(but);
+ AttributeDomain domain = (AttributeDomain)UI_but_datasetrow_domain_get(but);
+
+ const bool is_component_selected = (GeometryComponentType)
+ sspreadsheet->geometry_component_type == component;
+ const bool is_domain_selected = (AttributeDomain)sspreadsheet->attribute_domain == domain;
+ bool is_selected = is_component_selected && is_domain_selected;
+
+ if (component == GEO_COMPONENT_TYPE_INSTANCES) {
+ is_selected = is_component_selected;
+ }
+
+ return is_selected;
+}
+
+namespace blender::ed::spreadsheet {
+
+/* -------------------------------------------------------------------- */
+/* Draw Context */
+
+class DatasetDrawContext {
+ std::array<int, 2> mval_;
+
+ public:
+ const SpaceSpreadsheet *sspreadsheet;
+ Object *object_eval;
+ /* Current geometry set, changes per component. */
+ GeometrySet current_geometry_set;
+
+ DatasetDrawContext(const bContext *C);
+
+ GeometrySet geometry_set_from_component(GeometryComponentType component);
+ const std::array<int, 2> &cursor_mval() const;
+};
+
+DatasetDrawContext::DatasetDrawContext(const bContext *C)
+ : sspreadsheet(CTX_wm_space_spreadsheet(C)),
+ object_eval(spreadsheet_get_object_eval(sspreadsheet, CTX_data_depsgraph_pointer(C)))
+{
+ const wmWindow *win = CTX_wm_window(C);
+ const ARegion *region = CTX_wm_region(C);
+ mval_ = {win->eventstate->x - region->winrct.xmin, win->eventstate->y - region->winrct.ymin};
+}
+
+GeometrySet DatasetDrawContext::geometry_set_from_component(GeometryComponentType component)
+{
+ return spreadsheet_get_display_geometry_set(sspreadsheet, object_eval, component);
+}
+
+const std::array<int, 2> &DatasetDrawContext::cursor_mval() const
+{
+ return mval_;
+}
+
+/* -------------------------------------------------------------------- */
+/* Drawer */
+
+DatasetRegionDrawer::DatasetRegionDrawer(const ARegion *region,
+ uiBlock &block,
+ DatasetDrawContext &draw_context)
+ : row_height(UI_UNIT_Y),
+ xmin(region->v2d.cur.xmin),
+ xmax(region->v2d.cur.xmax),
+ block(block),
+ v2d(region->v2d),
+ draw_context(draw_context)
+{
+}
+
+void DatasetRegionDrawer::draw_hierarchy(const DatasetLayoutHierarchy &layout)
+{
+ for (const DatasetComponentLayoutInfo &component : layout.components) {
+ draw_context.current_geometry_set = draw_context.geometry_set_from_component(component.type);
+
+ draw_component_row(component);
+
+ /* Iterate attribute domains, skip unset ones (storage has to be in a enum-based, fixed size
+ * array so uses optionals to support skipping enum values that shouldn't be displayed for a
+ * component). */
+ for (const auto &optional_domain : component.attr_domains) {
+ if (!optional_domain) {
+ continue;
+ }
+
+ const DatasetAttrDomainLayoutInfo &domain_info = *optional_domain;
+ draw_attribute_domain_row(component, domain_info);
+ }
+ }
+}
+
+static int element_count_from_instances(const GeometrySet &geometry_set)
+{
+ if (geometry_set.has_instances()) {
+ const InstancesComponent *instances_component =
+ geometry_set.get_component_for_read<InstancesComponent>();
+ return instances_component->instances_amount();
+ }
+ return 0;
+}
+
+static int element_count_from_component_domain(const GeometrySet &geometry_set,
+ GeometryComponentType component,
+ AttributeDomain domain)
+{
+ if (geometry_set.has_mesh() && component == GEO_COMPONENT_TYPE_MESH) {
+ const MeshComponent *mesh_component = geometry_set.get_component_for_read<MeshComponent>();
+ return mesh_component->attribute_domain_size(domain);
+ }
+
+ if (geometry_set.has_pointcloud() && component == GEO_COMPONENT_TYPE_POINT_CLOUD) {
+ const PointCloudComponent *point_cloud_component =
+ geometry_set.get_component_for_read<PointCloudComponent>();
+ return point_cloud_component->attribute_domain_size(domain);
+ }
+
+ if (geometry_set.has_volume() && component == GEO_COMPONENT_TYPE_VOLUME) {
+ const VolumeComponent *volume_component =
+ geometry_set.get_component_for_read<VolumeComponent>();
+ return volume_component->attribute_domain_size(domain);
+ }
+
+ if (geometry_set.has_curve() && component == GEO_COMPONENT_TYPE_CURVE) {
+ const CurveComponent *curve_component = geometry_set.get_component_for_read<CurveComponent>();
+ return curve_component->attribute_domain_size(domain);
+ }
+
+ return 0;
+}
+
+void DatasetRegionDrawer::draw_dataset_row(const int indentation,
+ const GeometryComponentType component,
+ const std::optional<AttributeDomain> domain,
+ BIFIconID icon,
+ const char *label,
+ const bool is_active)
+{
+
+ const float row_height = UI_UNIT_Y;
+ const float padding_x = UI_UNIT_X * 0.25f;
+
+ const rctf rect = {float(xmin) + padding_x,
+ float(xmax) - V2D_SCROLL_HANDLE_WIDTH,
+ ymin_offset - row_height,
+ ymin_offset};
+
+ char element_count[7];
+ if (component == GEO_COMPONENT_TYPE_INSTANCES) {
+ BLI_str_format_attribute_domain_size(
+ element_count, element_count_from_instances(draw_context.current_geometry_set));
+ }
+ else {
+ BLI_str_format_attribute_domain_size(
+ element_count,
+ domain ? element_count_from_component_domain(
+ draw_context.current_geometry_set, component, *domain) :
+ 0);
+ }
+
+ std::string label_and_element_count = label;
+ label_and_element_count += UI_SEP_CHAR;
+ label_and_element_count += element_count;
+
+ uiBut *bt = uiDefIconTextButO(&block,
+ UI_BTYPE_DATASETROW,
+ "SPREADSHEET_OT_change_spreadsheet_data_source",
+ 0,
+ icon,
+ label,
+ rect.xmin,
+ rect.ymin,
+ BLI_rctf_size_x(&rect),
+ BLI_rctf_size_y(&rect),
+ nullptr);
+
+ UI_but_datasetrow_indentation_set(bt, indentation);
+
+ if (is_active) {
+ UI_but_hint_drawstr_set(bt, element_count);
+ UI_but_datasetrow_component_set(bt, component);
+ if (domain) {
+ UI_but_datasetrow_domain_set(bt, *domain);
+ }
+ UI_but_func_pushed_state_set(bt, &is_component_row_selected, draw_context.sspreadsheet);
+
+ PointerRNA *but_ptr = UI_but_operator_ptr_get((uiBut *)bt);
+ RNA_int_set(but_ptr, "component_type", component);
+ if (domain) {
+ RNA_int_set(but_ptr, "attribute_domain_type", *domain);
+ }
+ }
+
+ ymin_offset -= row_height;
+}
+
+void DatasetRegionDrawer::draw_component_row(const DatasetComponentLayoutInfo &component_info)
+{
+ if (component_info.type == GEO_COMPONENT_TYPE_INSTANCES) {
+ draw_dataset_row(
+ 0, component_info.type, std::nullopt, component_info.icon, component_info.label, true);
+ }
+ else {
+ draw_dataset_row(
+ 0, component_info.type, std::nullopt, component_info.icon, component_info.label, false);
+ }
+}
+
+void DatasetRegionDrawer::draw_attribute_domain_row(
+ const DatasetComponentLayoutInfo &component_info,
+ const DatasetAttrDomainLayoutInfo &domain_info)
+{
+ draw_dataset_row(
+ 1, component_info.type, domain_info.type, domain_info.icon, domain_info.label, true);
+}
+
+/* -------------------------------------------------------------------- */
+/* Drawer */
+
+void draw_dataset_in_region(const bContext *C, ARegion *region)
+{
+ DatasetDrawContext draw_context{C};
+ if (!draw_context.object_eval) {
+ /* No object means nothing to display. Keep the region empty. */
+ return;
+ }
+
+ uiBlock *block = UI_block_begin(C, region, __func__, UI_EMBOSS);
+
+ DatasetRegionDrawer drawer{region, *block, draw_context};
+
+ /* Start with an offset to align buttons to spreadsheet rows. Use spreadsheet drawing info for
+ * that. */
+ drawer.ymin_offset = -SpreadsheetDrawer().top_row_height + drawer.row_height;
+
+ const DatasetLayoutHierarchy hierarchy = dataset_layout_hierarchy();
+ drawer.draw_hierarchy(hierarchy);
+#ifndef NDEBUG
+ dataset_layout_hierarchy_sanity_check(hierarchy);
+#endif
+
+ UI_block_end(C, block);
+ UI_view2d_totRect_set(&region->v2d, region->winx, abs(drawer.ymin_offset));
+ UI_block_draw(C, block);
+}
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.hh b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.hh
new file mode 100644
index 00000000000..d9e6d882c2a
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.hh
@@ -0,0 +1,64 @@
+/*
+ * 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 <array>
+
+#include "BKE_geometry_set.hh"
+#include "UI_interface.h"
+#include "spreadsheet_dataset_layout.hh"
+
+struct ARegion;
+struct uiBlock;
+struct View2D;
+struct bContext;
+
+namespace blender::ed::spreadsheet {
+
+class DatasetDrawContext;
+
+class DatasetRegionDrawer {
+ public:
+ const int row_height;
+ float ymin_offset = 0;
+
+ int xmin;
+ int xmax;
+ uiBlock &block;
+ const View2D &v2d;
+ DatasetDrawContext &draw_context;
+
+ DatasetRegionDrawer(const ARegion *region, uiBlock &block, DatasetDrawContext &draw_context);
+
+ void draw_hierarchy(const DatasetLayoutHierarchy &layout);
+
+ void draw_attribute_domain_row(const DatasetComponentLayoutInfo &component,
+ const DatasetAttrDomainLayoutInfo &domain_info);
+ void draw_component_row(const DatasetComponentLayoutInfo &component_info);
+
+ private:
+ void draw_dataset_row(const int indentation,
+ const GeometryComponentType component,
+ const std::optional<AttributeDomain> domain,
+ const BIFIconID icon,
+ const char *label,
+ const bool is_active);
+};
+
+void draw_dataset_in_region(const bContext *C, ARegion *region);
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_layout.cc
new file mode 100644
index 00000000000..abbad8c7088
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_layout.cc
@@ -0,0 +1,112 @@
+/*
+ * 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 <optional>
+
+#include "BLI_span.hh"
+
+#include "BLT_translation.h"
+
+#include "spreadsheet_dataset_layout.hh"
+
+namespace blender::ed::spreadsheet {
+
+#define ATTR_INFO(type, label, icon) \
+ std::optional<DatasetAttrDomainLayoutInfo> \
+ { \
+ std::in_place, type, label, icon \
+ }
+#define ATTR_INFO_NONE(type) \
+ { \
+ std::nullopt \
+ }
+
+/**
+ * Definition for the component->attribute-domain hierarchy.
+ * Constructed at compile time.
+ *
+ * \warning Order of attribute-domains matters! It __must__ match the #AttributeDomain
+ * definition and fill gaps with unset optionals (i.e. `std::nullopt`). Would be nice to use
+ * array designators for this (which C++ doesn't support).
+ */
+constexpr DatasetComponentLayoutInfo DATASET_layout_hierarchy[] = {
+ {
+ GEO_COMPONENT_TYPE_MESH,
+ N_("Mesh"),
+ ICON_MESH_DATA,
+ {
+ ATTR_INFO(ATTR_DOMAIN_POINT, N_("Vertex"), ICON_VERTEXSEL),
+ ATTR_INFO(ATTR_DOMAIN_EDGE, N_("Edge"), ICON_EDGESEL),
+ ATTR_INFO(ATTR_DOMAIN_FACE, N_("Face"), ICON_FACESEL),
+ ATTR_INFO(ATTR_DOMAIN_CORNER, N_("Face Corner"), ICON_NODE_CORNER),
+ },
+ },
+ {
+ GEO_COMPONENT_TYPE_CURVE,
+ N_("Curves"),
+ ICON_CURVE_DATA,
+ {
+ ATTR_INFO(ATTR_DOMAIN_POINT, N_("Control Point"), ICON_CURVE_BEZCIRCLE),
+ ATTR_INFO_NONE(ATTR_DOMAIN_EDGE),
+ ATTR_INFO_NONE(ATTR_DOMAIN_CORNER),
+ ATTR_INFO_NONE(ATTR_DOMAIN_FACE),
+ ATTR_INFO(ATTR_DOMAIN_CURVE, N_("Spline"), ICON_CURVE_PATH),
+ },
+ },
+ {
+ GEO_COMPONENT_TYPE_POINT_CLOUD,
+ N_("Point Cloud"),
+ ICON_POINTCLOUD_DATA,
+ {
+ ATTR_INFO(ATTR_DOMAIN_POINT, N_("Point"), ICON_PARTICLE_POINT),
+ },
+ },
+ {
+ GEO_COMPONENT_TYPE_INSTANCES,
+ N_("Instances"),
+ ICON_EMPTY_AXIS,
+ {},
+ },
+};
+
+#undef ATTR_INFO
+#undef ATTR_INFO_LABEL
+
+DatasetLayoutHierarchy dataset_layout_hierarchy()
+{
+ return DatasetLayoutHierarchy{
+ Span{DATASET_layout_hierarchy, ARRAY_SIZE(DATASET_layout_hierarchy)}};
+}
+
+#ifndef NDEBUG
+/**
+ * Debug-only sanity check for correct attribute domain initialization (order/indices must
+ * match AttributeDomain). This doesn't check for all possible missuses, but should catch the most
+ * likely mistakes.
+ */
+void dataset_layout_hierarchy_sanity_check(const DatasetLayoutHierarchy &hierarchy)
+{
+ for (const DatasetComponentLayoutInfo &component : hierarchy.components) {
+ for (uint i = 0; i < component.attr_domains.size(); i++) {
+ if (component.attr_domains[i]) {
+ BLI_assert(component.attr_domains[i]->type == static_cast<AttributeDomain>(i));
+ }
+ }
+ }
+}
+#endif
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_layout.hh b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_layout.hh
new file mode 100644
index 00000000000..d463739a0fa
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_layout.hh
@@ -0,0 +1,68 @@
+/*
+ * 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 <array>
+#include <optional>
+
+/* Enum definitions... */
+#include "BKE_attribute.h"
+#include "BKE_geometry_set.h"
+
+#include "BLI_span.hh"
+
+/* More enum definitions... */
+#include "UI_resources.h"
+
+#pragma once
+
+namespace blender::ed::spreadsheet {
+
+struct DatasetAttrDomainLayoutInfo {
+ AttributeDomain type;
+ const char *label;
+ BIFIconID icon;
+
+ constexpr DatasetAttrDomainLayoutInfo(AttributeDomain type, const char *label, BIFIconID icon)
+ : type(type), label(label), icon(icon)
+ {
+ }
+};
+
+struct DatasetComponentLayoutInfo {
+ GeometryComponentType type;
+ const char *label;
+ BIFIconID icon;
+ /** Array of attribute-domains. Has to be fixed size based on #AttributeDomain enum, but not all
+ * values need displaying for all parent components. Hence the optional use. */
+ using AttrDomainArray = std::array<std::optional<DatasetAttrDomainLayoutInfo>, ATTR_DOMAIN_NUM>;
+ const AttrDomainArray attr_domains;
+};
+
+struct DatasetLayoutHierarchy {
+ /** The components for display (with layout info like icon and label). Each component stores
+ * the attribute domains it wants to display (also with layout info like icon and label). */
+ const Span<DatasetComponentLayoutInfo> components;
+};
+
+DatasetLayoutHierarchy dataset_layout_hierarchy();
+
+#ifndef NDEBUG
+void dataset_layout_hierarchy_sanity_check(const DatasetLayoutHierarchy &hierarchy);
+#endif
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh b/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh
index 7e3b79a6706..8be5283fd63 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh
@@ -16,10 +16,23 @@
#pragma once
+#include "BKE_geometry_set.hh"
+
typedef struct SpaceSpreadsheet_Runtime {
int visible_rows;
int tot_rows;
int tot_columns;
} SpaceSpreadsheet_Runtime;
+struct bContext;
+
void spreadsheet_operatortypes(void);
+void spreadsheet_update_context_path(const bContext *C);
+Object *spreadsheet_get_object_eval(const SpaceSpreadsheet *sspreadsheet,
+ const Depsgraph *depsgraph);
+
+namespace blender::ed::spreadsheet {
+GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *sspreadsheet,
+ Object *object_eval,
+ const GeometryComponentType used_component_type);
+}
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc
index f1ca65817f6..8079763a339 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc
@@ -170,7 +170,7 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer {
this->draw_float_vector(params, Span(&value.x, 3));
}
else if (cell_value.value_color.has_value()) {
- const Color4f value = *cell_value.value_color;
+ const ColorGeometry4f value = *cell_value.value_color;
this->draw_float_vector(params, Span(&value.r, 4));
}
else if (cell_value.value_object.has_value()) {
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_ops.cc b/source/blender/editors/space_spreadsheet/spreadsheet_ops.cc
index 770bd207e8d..11ed88b7dc9 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_ops.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_ops.cc
@@ -13,9 +13,137 @@
* 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_screen.h"
+
+#include "DNA_space_types.h"
+
+#include "ED_screen.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "BLI_listbase.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BKE_context.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "ED_screen.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
#include "spreadsheet_intern.hh"
+#include "spreadsheet_row_filter.hh"
+
+using namespace blender::ed::spreadsheet;
+
+static int row_filter_add_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
+
+ SpreadsheetRowFilter *row_filter = spreadsheet_row_filter_new();
+ BLI_addtail(&sspreadsheet->row_filters, row_filter);
+
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_SPREADSHEET, sspreadsheet);
+
+ return OPERATOR_FINISHED;
+}
+
+static void SPREADSHEET_OT_add_row_filter_rule(wmOperatorType *ot)
+{
+ ot->name = "Add Row Filter";
+ ot->description = "Add a filter to remove rows from the displayed data";
+ ot->idname = "SPREADSHEET_OT_add_row_filter_rule";
+
+ ot->exec = row_filter_add_exec;
+ ot->poll = ED_operator_spreadsheet_active;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+static int row_filter_remove_exec(bContext *C, wmOperator *op)
+{
+ SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
+
+ SpreadsheetRowFilter *row_filter = (SpreadsheetRowFilter *)BLI_findlink(
+ &sspreadsheet->row_filters, RNA_int_get(op->ptr, "index"));
+ if (row_filter == nullptr) {
+ return OPERATOR_CANCELLED;
+ }
+
+ BLI_remlink(&sspreadsheet->row_filters, row_filter);
+ spreadsheet_row_filter_free(row_filter);
+
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_SPREADSHEET, sspreadsheet);
+
+ return OPERATOR_FINISHED;
+}
+
+static void SPREADSHEET_OT_remove_row_filter_rule(wmOperatorType *ot)
+{
+ ot->name = "Remove Row Filter";
+ ot->description = "Remove a row filter from the rules";
+ ot->idname = "SPREADSHEET_OT_remove_row_filter_rule";
+
+ ot->exec = row_filter_remove_exec;
+ ot->poll = ED_operator_spreadsheet_active;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, INT_MAX);
+}
+
+static int select_component_domain_invoke(bContext *C,
+ wmOperator *op,
+ const wmEvent *UNUSED(event))
+{
+ GeometryComponentType component_type = static_cast<GeometryComponentType>(
+ RNA_int_get(op->ptr, "component_type"));
+ AttributeDomain attribute_domain = static_cast<AttributeDomain>(
+ RNA_int_get(op->ptr, "attribute_domain_type"));
+
+ SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
+ sspreadsheet->geometry_component_type = component_type;
+ sspreadsheet->attribute_domain = attribute_domain;
+
+ /* Refresh header and main region. */
+ WM_main_add_notifier(NC_SPACE | ND_SPACE_SPREADSHEET, nullptr);
+
+ return OPERATOR_FINISHED;
+}
+
+static void SPREADSHEET_OT_change_spreadsheet_data_source(wmOperatorType *ot)
+{
+ ot->name = "Change Visible Data Source";
+ ot->description = "Change visible data source in the spreadsheet";
+ ot->idname = "SPREADSHEET_OT_change_spreadsheet_data_source";
+
+ ot->invoke = select_component_domain_invoke;
+
+ RNA_def_int(ot->srna, "component_type", 0, 0, INT16_MAX, "Component Type", "", 0, INT16_MAX);
+ RNA_def_int(ot->srna,
+ "attribute_domain_type",
+ 0,
+ 0,
+ INT16_MAX,
+ "Attribute Domain Type",
+ "",
+ 0,
+ INT16_MAX);
+
+ ot->flag = OPTYPE_INTERNAL;
+}
void spreadsheet_operatortypes()
{
+ WM_operatortype_append(SPREADSHEET_OT_add_row_filter_rule);
+ WM_operatortype_append(SPREADSHEET_OT_remove_row_filter_rule);
+ WM_operatortype_append(SPREADSHEET_OT_change_spreadsheet_data_source);
}
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc
new file mode 100644
index 00000000000..ae336edfead
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc
@@ -0,0 +1,366 @@
+/*
+ * 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 <cstring>
+
+#include "BLI_listbase.h"
+
+#include "DNA_collection_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
+#include "spreadsheet_intern.hh"
+
+#include "spreadsheet_data_source_geometry.hh"
+#include "spreadsheet_intern.hh"
+#include "spreadsheet_layout.hh"
+#include "spreadsheet_row_filter.hh"
+
+namespace blender::ed::spreadsheet {
+
+template<typename OperationFn>
+static void apply_filter_operation(const ColumnValues &values,
+ OperationFn check_fn,
+ MutableSpan<bool> rows_included)
+{
+ for (const int i : rows_included.index_range()) {
+ if (!rows_included[i]) {
+ continue;
+ }
+ CellValue cell_value;
+ values.get_value(i, cell_value);
+ if (!check_fn(cell_value)) {
+ rows_included[i] = false;
+ }
+ }
+}
+
+static void apply_row_filter(const SpreadsheetLayout &spreadsheet_layout,
+ const SpreadsheetRowFilter &row_filter,
+ MutableSpan<bool> rows_included)
+{
+ for (const ColumnLayout &column : spreadsheet_layout.columns) {
+ const ColumnValues &values = *column.values;
+ if (values.name() != row_filter.column_name) {
+ continue;
+ }
+
+ switch (values.type()) {
+ case SPREADSHEET_VALUE_TYPE_INT32: {
+ const int value = row_filter.value_int;
+ switch (row_filter.operation) {
+ case SPREADSHEET_ROW_FILTER_EQUAL: {
+ apply_filter_operation(
+ values,
+ [value](const CellValue &cell_value) -> bool {
+ return *cell_value.value_int == value;
+ },
+ rows_included);
+ break;
+ }
+ case SPREADSHEET_ROW_FILTER_GREATER: {
+ apply_filter_operation(
+ values,
+ [value](const CellValue &cell_value) -> bool {
+ return *cell_value.value_int > value;
+ },
+ rows_included);
+ break;
+ }
+ case SPREADSHEET_ROW_FILTER_LESS: {
+ apply_filter_operation(
+ values,
+ [value](const CellValue &cell_value) -> bool {
+ return *cell_value.value_int < value;
+ },
+ rows_included);
+ break;
+ }
+ }
+ break;
+ }
+ case SPREADSHEET_VALUE_TYPE_FLOAT: {
+ const float value = row_filter.value_float;
+ switch (row_filter.operation) {
+ case SPREADSHEET_ROW_FILTER_EQUAL: {
+ const float threshold = row_filter.threshold;
+ apply_filter_operation(
+ values,
+ [value, threshold](const CellValue &cell_value) -> bool {
+ return std::abs(*cell_value.value_float - value) < threshold;
+ },
+ rows_included);
+ break;
+ }
+ case SPREADSHEET_ROW_FILTER_GREATER: {
+ apply_filter_operation(
+ values,
+ [value](const CellValue &cell_value) -> bool {
+ return *cell_value.value_float > value;
+ },
+ rows_included);
+ break;
+ }
+ case SPREADSHEET_ROW_FILTER_LESS: {
+ apply_filter_operation(
+ values,
+ [value](const CellValue &cell_value) -> bool {
+ return *cell_value.value_float < value;
+ },
+ rows_included);
+ break;
+ }
+ }
+ break;
+ }
+ case SPREADSHEET_VALUE_TYPE_FLOAT2: {
+ const float2 value = row_filter.value_float2;
+ switch (row_filter.operation) {
+ case SPREADSHEET_ROW_FILTER_EQUAL: {
+ const float threshold_squared = row_filter.threshold * row_filter.threshold;
+ apply_filter_operation(
+ values,
+ [value, threshold_squared](const CellValue &cell_value) -> bool {
+ return float2::distance_squared(*cell_value.value_float2, value) <
+ threshold_squared;
+ },
+ rows_included);
+ break;
+ }
+ case SPREADSHEET_ROW_FILTER_GREATER: {
+ apply_filter_operation(
+ values,
+ [value](const CellValue &cell_value) -> bool {
+ return cell_value.value_float2->x > value.x &&
+ cell_value.value_float2->y > value.y;
+ },
+ rows_included);
+ break;
+ }
+ case SPREADSHEET_ROW_FILTER_LESS: {
+ apply_filter_operation(
+ values,
+ [value](const CellValue &cell_value) -> bool {
+ return cell_value.value_float2->x < value.x &&
+ cell_value.value_float2->y < value.y;
+ },
+ rows_included);
+ break;
+ }
+ }
+ break;
+ }
+ case SPREADSHEET_VALUE_TYPE_FLOAT3: {
+ const float3 value = row_filter.value_float3;
+ switch (row_filter.operation) {
+ case SPREADSHEET_ROW_FILTER_EQUAL: {
+ const float threshold_squared = row_filter.threshold * row_filter.threshold;
+ apply_filter_operation(
+ values,
+ [value, threshold_squared](const CellValue &cell_value) -> bool {
+ return float3::distance_squared(*cell_value.value_float3, value) <
+ threshold_squared;
+ },
+ rows_included);
+ break;
+ }
+ case SPREADSHEET_ROW_FILTER_GREATER: {
+ apply_filter_operation(
+ values,
+ [value](const CellValue &cell_value) -> bool {
+ return cell_value.value_float3->x > value.x &&
+ cell_value.value_float3->y > value.y &&
+ cell_value.value_float3->z > value.z;
+ },
+ rows_included);
+ break;
+ }
+ case SPREADSHEET_ROW_FILTER_LESS: {
+ apply_filter_operation(
+ values,
+ [value](const CellValue &cell_value) -> bool {
+ return cell_value.value_float3->x < value.x &&
+ cell_value.value_float3->y < value.y &&
+ cell_value.value_float3->z < value.z;
+ },
+ rows_included);
+ break;
+ }
+ }
+ break;
+ }
+ case SPREADSHEET_VALUE_TYPE_COLOR: {
+ const ColorGeometry4f value = row_filter.value_color;
+ switch (row_filter.operation) {
+ case SPREADSHEET_ROW_FILTER_EQUAL: {
+ const float threshold_squared = row_filter.threshold * row_filter.threshold;
+ apply_filter_operation(
+ values,
+ [value, threshold_squared](const CellValue &cell_value) -> bool {
+ return len_squared_v4v4(value, *cell_value.value_color) < threshold_squared;
+ },
+ rows_included);
+ break;
+ }
+ }
+ break;
+ }
+ case SPREADSHEET_VALUE_TYPE_BOOL: {
+ const bool value = (row_filter.flag & SPREADSHEET_ROW_FILTER_BOOL_VALUE) != 0;
+ apply_filter_operation(
+ values,
+ [value](const CellValue &cell_value) -> bool {
+ return *cell_value.value_bool == value;
+ },
+ rows_included);
+ break;
+ }
+ case SPREADSHEET_VALUE_TYPE_INSTANCES: {
+ const StringRef value = row_filter.value_string;
+ apply_filter_operation(
+ values,
+ [value](const CellValue &cell_value) -> bool {
+ const ID *id = nullptr;
+ if (cell_value.value_object) {
+ id = &cell_value.value_object->object->id;
+ }
+ else if (cell_value.value_collection) {
+ id = &cell_value.value_collection->collection->id;
+ }
+ if (id == nullptr) {
+ return false;
+ }
+
+ return value == id->name + 2;
+ },
+ rows_included);
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Only one column should have this name. */
+ break;
+ }
+}
+
+static void index_vector_from_bools(Span<bool> selection, Vector<int64_t> &indices)
+{
+ for (const int i : selection.index_range()) {
+ if (selection[i]) {
+ indices.append(i);
+ }
+ }
+}
+
+static bool use_row_filters(const SpaceSpreadsheet &sspreadsheet)
+{
+ if (!(sspreadsheet.filter_flag & SPREADSHEET_FILTER_ENABLE)) {
+ return false;
+ }
+ if (BLI_listbase_is_empty(&sspreadsheet.row_filters)) {
+ return false;
+ }
+ return true;
+}
+
+static bool use_selection_filter(const SpaceSpreadsheet &sspreadsheet,
+ const DataSource &data_source)
+{
+ if (!(sspreadsheet.filter_flag & SPREADSHEET_FILTER_SELECTED_ONLY)) {
+ return false;
+ }
+ if (!data_source.has_selection_filter()) {
+ return false;
+ }
+ return true;
+}
+
+Span<int64_t> spreadsheet_filter_rows(const SpaceSpreadsheet &sspreadsheet,
+ const SpreadsheetLayout &spreadsheet_layout,
+ const DataSource &data_source,
+ ResourceScope &scope)
+{
+ const int tot_rows = data_source.tot_rows();
+
+ const bool use_selection = use_selection_filter(sspreadsheet, data_source);
+ const bool use_filters = use_row_filters(sspreadsheet);
+
+ /* Avoid allocating an array if no row filtering is necessary. */
+ if (!(use_filters || use_selection)) {
+ return IndexRange(tot_rows).as_span();
+ }
+
+ Array<bool> rows_included(tot_rows, true);
+
+ if (use_filters) {
+ LISTBASE_FOREACH (const SpreadsheetRowFilter *, row_filter, &sspreadsheet.row_filters) {
+ if (row_filter->flag & SPREADSHEET_ROW_FILTER_ENABLED) {
+ apply_row_filter(spreadsheet_layout, *row_filter, rows_included);
+ }
+ }
+ }
+
+ if (use_selection) {
+ const GeometryDataSource *geometry_data_source = dynamic_cast<const GeometryDataSource *>(
+ &data_source);
+ geometry_data_source->apply_selection_filter(rows_included);
+ }
+
+ Vector<int64_t> &indices = scope.construct<Vector<int64_t>>(__func__);
+ index_vector_from_bools(rows_included, indices);
+
+ return indices;
+}
+
+SpreadsheetRowFilter *spreadsheet_row_filter_new()
+{
+ SpreadsheetRowFilter *row_filter = (SpreadsheetRowFilter *)MEM_callocN(
+ sizeof(SpreadsheetRowFilter), __func__);
+ row_filter->flag = (SPREADSHEET_ROW_FILTER_UI_EXPAND | SPREADSHEET_ROW_FILTER_ENABLED);
+ row_filter->operation = SPREADSHEET_ROW_FILTER_LESS;
+ row_filter->threshold = 0.01f;
+ row_filter->column_name[0] = '\0';
+
+ return row_filter;
+}
+
+SpreadsheetRowFilter *spreadsheet_row_filter_copy(const SpreadsheetRowFilter *src_row_filter)
+{
+ SpreadsheetRowFilter *new_filter = spreadsheet_row_filter_new();
+
+ memcpy(new_filter, src_row_filter, sizeof(SpreadsheetRowFilter));
+ new_filter->next = nullptr;
+ new_filter->prev = nullptr;
+
+ return new_filter;
+}
+
+void spreadsheet_row_filter_free(SpreadsheetRowFilter *row_filter)
+{
+ MEM_SAFE_FREE(row_filter->value_string);
+ MEM_freeN(row_filter);
+}
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.hh b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.hh
new file mode 100644
index 00000000000..0a5783e318d
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.hh
@@ -0,0 +1,35 @@
+/*
+ * 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_resource_scope.hh"
+
+#include "spreadsheet_data_source.hh"
+#include "spreadsheet_layout.hh"
+
+namespace blender::ed::spreadsheet {
+
+Span<int64_t> spreadsheet_filter_rows(const SpaceSpreadsheet &sspreadsheet,
+ const SpreadsheetLayout &spreadsheet_layout,
+ const DataSource &data_source,
+ ResourceScope &scope);
+
+SpreadsheetRowFilter *spreadsheet_row_filter_new();
+SpreadsheetRowFilter *spreadsheet_row_filter_copy(const SpreadsheetRowFilter *src_row_filter);
+void spreadsheet_row_filter_free(SpreadsheetRowFilter *row_filter);
+
+} // namespace blender::ed::spreadsheet
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc
new file mode 100644
index 00000000000..219d03c1dcd
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc
@@ -0,0 +1,347 @@
+/*
+ * 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 <cstring>
+
+#include "BLI_listbase.h"
+#include "BLI_string.h"
+#include "BLI_string_ref.hh"
+
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+
+#include "BKE_screen.h"
+
+#include "RNA_access.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "BLT_translation.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "spreadsheet_column.hh"
+#include "spreadsheet_intern.hh"
+#include "spreadsheet_row_filter.hh"
+#include "spreadsheet_row_filter_ui.hh"
+
+using namespace blender;
+using namespace blender::ed::spreadsheet;
+
+static void filter_panel_id_fn(void *UNUSED(row_filter_v), char *r_name)
+{
+ /* All row filters use the same panel ID. */
+ BLI_snprintf(r_name, BKE_ST_MAXNAME, "SPREADSHEET_PT_filter");
+}
+
+static std::string operation_string(const eSpreadsheetColumnValueType data_type,
+ const eSpreadsheetFilterOperation operation)
+{
+ if (ELEM(data_type,
+ SPREADSHEET_VALUE_TYPE_BOOL,
+ SPREADSHEET_VALUE_TYPE_INSTANCES,
+ SPREADSHEET_VALUE_TYPE_COLOR)) {
+ return "=";
+ }
+
+ switch (operation) {
+ case SPREADSHEET_ROW_FILTER_EQUAL:
+ return "=";
+ case SPREADSHEET_ROW_FILTER_GREATER:
+ return ">";
+ case SPREADSHEET_ROW_FILTER_LESS:
+ return "<";
+ }
+ BLI_assert_unreachable();
+ return "";
+}
+
+static std::string value_string(const SpreadsheetRowFilter &row_filter,
+ const eSpreadsheetColumnValueType data_type)
+{
+ switch (data_type) {
+ case SPREADSHEET_VALUE_TYPE_INT32:
+ return std::to_string(row_filter.value_int);
+ case SPREADSHEET_VALUE_TYPE_FLOAT: {
+ std::ostringstream result;
+ result.precision(3);
+ result << std::fixed << row_filter.value_float;
+ return result.str();
+ }
+ case SPREADSHEET_VALUE_TYPE_FLOAT2: {
+ std::ostringstream result;
+ result.precision(3);
+ result << std::fixed << "(" << row_filter.value_float2[0] << ", "
+ << row_filter.value_float2[1] << ")";
+ return result.str();
+ }
+ case SPREADSHEET_VALUE_TYPE_FLOAT3: {
+ std::ostringstream result;
+ result.precision(3);
+ result << std::fixed << "(" << row_filter.value_float3[0] << ", "
+ << row_filter.value_float3[1] << ", " << row_filter.value_float3[2] << ")";
+ return result.str();
+ }
+ case SPREADSHEET_VALUE_TYPE_BOOL:
+ return (row_filter.flag & SPREADSHEET_ROW_FILTER_BOOL_VALUE) ? IFACE_("True") :
+ IFACE_("False");
+ case SPREADSHEET_VALUE_TYPE_INSTANCES:
+ if (row_filter.value_string != nullptr) {
+ return row_filter.value_string;
+ }
+ return "";
+ case SPREADSHEET_VALUE_TYPE_COLOR:
+ std::ostringstream result;
+ result.precision(3);
+ result << std::fixed << "(" << row_filter.value_color[0] << ", " << row_filter.value_color[1]
+ << ", " << row_filter.value_color[2] << ", " << row_filter.value_color[3] << ")";
+ return result.str();
+ }
+ BLI_assert_unreachable();
+ return "";
+}
+
+static SpreadsheetColumn *lookup_visible_column_for_filter(const SpaceSpreadsheet &sspreadsheet,
+ const StringRef column_name)
+{
+ LISTBASE_FOREACH (SpreadsheetColumn *, column, &sspreadsheet.columns) {
+ if (column->display_name == column_name) {
+ return column;
+ }
+ }
+ return nullptr;
+}
+
+static void spreadsheet_filter_panel_draw_header(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+ SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
+ PointerRNA *filter_ptr = UI_panel_custom_data_get(panel);
+ const SpreadsheetRowFilter *filter = (SpreadsheetRowFilter *)filter_ptr->data;
+ const StringRef column_name = filter->column_name;
+ const eSpreadsheetFilterOperation operation = (eSpreadsheetFilterOperation)filter->operation;
+
+ const SpreadsheetColumn *column = lookup_visible_column_for_filter(*sspreadsheet, column_name);
+ if (!(sspreadsheet->filter_flag & SPREADSHEET_FILTER_ENABLE) ||
+ (column == nullptr && !column_name.is_empty())) {
+ uiLayoutSetActive(layout, false);
+ }
+
+ uiLayout *row = uiLayoutRow(layout, true);
+ uiLayoutSetEmboss(row, UI_EMBOSS_NONE);
+ uiItemR(row, filter_ptr, "enabled", UI_ITEM_R_ICON_ONLY, "", ICON_NONE);
+
+ if (column_name.is_empty()) {
+ uiItemL(row, IFACE_("Filter"), ICON_NONE);
+ }
+ else if (column == nullptr) {
+ uiItemL(row, column_name.data(), ICON_NONE);
+ }
+ else {
+ const eSpreadsheetColumnValueType data_type = (eSpreadsheetColumnValueType)column->data_type;
+ std::stringstream ss;
+ ss << column_name;
+ ss << " ";
+ ss << operation_string(data_type, operation);
+ ss << " ";
+ ss << value_string(*filter, data_type);
+ uiItemL(row, ss.str().c_str(), ICON_NONE);
+ }
+
+ row = uiLayoutRow(layout, true);
+ uiLayoutSetEmboss(row, UI_EMBOSS_NONE);
+ const int current_index = BLI_findindex(&sspreadsheet->row_filters, filter);
+ uiItemIntO(row, "", ICON_X, "SPREADSHEET_OT_remove_row_filter_rule", "index", current_index);
+
+ /* Some padding so the X isn't too close to the drag icon. */
+ uiItemS_ex(layout, 0.25f);
+}
+
+static void spreadsheet_filter_panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+ SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
+ PointerRNA *filter_ptr = UI_panel_custom_data_get(panel);
+ SpreadsheetRowFilter *filter = (SpreadsheetRowFilter *)filter_ptr->data;
+ const StringRef column_name = filter->column_name;
+ const eSpreadsheetFilterOperation operation = (eSpreadsheetFilterOperation)filter->operation;
+
+ const SpreadsheetColumn *column = lookup_visible_column_for_filter(*sspreadsheet, column_name);
+ if (!(sspreadsheet->filter_flag & SPREADSHEET_FILTER_ENABLE) ||
+ !(filter->flag & SPREADSHEET_ROW_FILTER_ENABLED) ||
+ (column == nullptr && !column_name.is_empty())) {
+ uiLayoutSetActive(layout, false);
+ }
+
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+
+ uiItemR(layout, filter_ptr, "column_name", 0, IFACE_("Column"), ICON_NONE);
+
+ /* Don't draw settings for filters with no corresponding visible column. */
+ if (column == nullptr || column_name.is_empty()) {
+ return;
+ }
+
+ switch (static_cast<eSpreadsheetColumnValueType>(column->data_type)) {
+ case SPREADSHEET_VALUE_TYPE_INT32:
+ uiItemR(layout, filter_ptr, "operation", 0, nullptr, ICON_NONE);
+ uiItemR(layout, filter_ptr, "value_int", 0, IFACE_("Value"), ICON_NONE);
+ break;
+ case SPREADSHEET_VALUE_TYPE_FLOAT:
+ uiItemR(layout, filter_ptr, "operation", 0, nullptr, ICON_NONE);
+ uiItemR(layout, filter_ptr, "value_float", 0, IFACE_("Value"), ICON_NONE);
+ if (operation == SPREADSHEET_ROW_FILTER_EQUAL) {
+ uiItemR(layout, filter_ptr, "threshold", 0, nullptr, ICON_NONE);
+ }
+ break;
+ case SPREADSHEET_VALUE_TYPE_FLOAT2:
+ uiItemR(layout, filter_ptr, "operation", 0, nullptr, ICON_NONE);
+ uiItemR(layout, filter_ptr, "value_float2", 0, IFACE_("Value"), ICON_NONE);
+ if (operation == SPREADSHEET_ROW_FILTER_EQUAL) {
+ uiItemR(layout, filter_ptr, "threshold", 0, nullptr, ICON_NONE);
+ }
+ break;
+ case SPREADSHEET_VALUE_TYPE_FLOAT3:
+ uiItemR(layout, filter_ptr, "operation", 0, nullptr, ICON_NONE);
+ uiItemR(layout, filter_ptr, "value_float3", 0, IFACE_("Value"), ICON_NONE);
+ if (operation == SPREADSHEET_ROW_FILTER_EQUAL) {
+ uiItemR(layout, filter_ptr, "threshold", 0, nullptr, ICON_NONE);
+ }
+ break;
+ case SPREADSHEET_VALUE_TYPE_BOOL:
+ uiItemR(layout, filter_ptr, "value_boolean", 0, IFACE_("Value"), ICON_NONE);
+ break;
+ case SPREADSHEET_VALUE_TYPE_INSTANCES:
+ uiItemR(layout, filter_ptr, "value_string", 0, IFACE_("Value"), ICON_NONE);
+ break;
+ case SPREADSHEET_VALUE_TYPE_COLOR:
+ uiItemR(layout, filter_ptr, "value_color", 0, IFACE_("Value"), ICON_NONE);
+ uiItemR(layout, filter_ptr, "threshold", 0, nullptr, ICON_NONE);
+ break;
+ }
+}
+
+static void spreadsheet_row_filters_layout(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+ ARegion *region = CTX_wm_region(C);
+ bScreen *screen = CTX_wm_screen(C);
+ SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
+ ListBase *row_filters = &sspreadsheet->row_filters;
+
+ if (!(sspreadsheet->filter_flag & SPREADSHEET_FILTER_ENABLE)) {
+ uiLayoutSetActive(layout, false);
+ }
+
+ uiItemO(layout, nullptr, ICON_ADD, "SPREADSHEET_OT_add_row_filter_rule");
+
+ const bool panels_match = UI_panel_list_matches_data(region, row_filters, filter_panel_id_fn);
+
+ if (!panels_match) {
+ UI_panels_free_instanced(C, region);
+ LISTBASE_FOREACH (SpreadsheetRowFilter *, row_filter, row_filters) {
+ char panel_idname[MAX_NAME];
+ filter_panel_id_fn(row_filter, panel_idname);
+
+ PointerRNA *filter_ptr = (PointerRNA *)MEM_mallocN(sizeof(PointerRNA), "panel customdata");
+ RNA_pointer_create(&screen->id, &RNA_SpreadsheetRowFilter, row_filter, filter_ptr);
+
+ UI_panel_add_instanced(C, region, &region->panels, panel_idname, filter_ptr);
+ }
+ }
+ else {
+ /* Assuming there's only one group of instanced panels, update the custom data pointers. */
+ Panel *panel_iter = (Panel *)region->panels.first;
+ LISTBASE_FOREACH (SpreadsheetRowFilter *, row_filter, row_filters) {
+
+ /* Move to the next instanced panel corresponding to the next filter. */
+ while ((panel_iter->type == nullptr) || !(panel_iter->type->flag & PANEL_TYPE_INSTANCED)) {
+ panel_iter = panel_iter->next;
+ BLI_assert(panel_iter != nullptr); /* There shouldn't be fewer panels than filters. */
+ }
+
+ PointerRNA *filter_ptr = (PointerRNA *)MEM_mallocN(sizeof(PointerRNA), "panel customdata");
+ RNA_pointer_create(&screen->id, &RNA_SpreadsheetRowFilter, row_filter, filter_ptr);
+ UI_panel_custom_data_set(panel_iter, filter_ptr);
+
+ panel_iter = panel_iter->next;
+ }
+ }
+}
+
+static void filter_reorder(bContext *C, Panel *panel, int new_index)
+{
+ SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
+ ListBase *row_filters = &sspreadsheet->row_filters;
+ PointerRNA *filter_ptr = UI_panel_custom_data_get(panel);
+ SpreadsheetRowFilter *filter = (SpreadsheetRowFilter *)filter_ptr->data;
+
+ int current_index = BLI_findindex(row_filters, filter);
+ BLI_assert(current_index >= 0);
+ BLI_assert(new_index >= 0);
+
+ BLI_listbase_link_move(row_filters, filter, new_index - current_index);
+}
+
+static short get_filter_expand_flag(const bContext *UNUSED(C), Panel *panel)
+{
+ PointerRNA *filter_ptr = UI_panel_custom_data_get(panel);
+ SpreadsheetRowFilter *filter = (SpreadsheetRowFilter *)filter_ptr->data;
+
+ return (short)filter->flag & SPREADSHEET_ROW_FILTER_UI_EXPAND;
+}
+
+static void set_filter_expand_flag(const bContext *UNUSED(C), Panel *panel, short expand_flag)
+{
+ PointerRNA *filter_ptr = UI_panel_custom_data_get(panel);
+ SpreadsheetRowFilter *filter = (SpreadsheetRowFilter *)filter_ptr->data;
+
+ SET_FLAG_FROM_TEST(filter->flag,
+ expand_flag & SPREADSHEET_ROW_FILTER_UI_EXPAND,
+ SPREADSHEET_ROW_FILTER_UI_EXPAND);
+}
+
+void register_row_filter_panels(ARegionType &region_type)
+{
+ {
+ PanelType *panel_type = (PanelType *)MEM_callocN(sizeof(PanelType), __func__);
+ strcpy(panel_type->idname, "SPREADSHEET_PT_row_filters");
+ strcpy(panel_type->label, N_("Filters"));
+ strcpy(panel_type->category, "Filters");
+ strcpy(panel_type->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
+ panel_type->flag = PANEL_TYPE_NO_HEADER;
+ panel_type->draw = spreadsheet_row_filters_layout;
+ BLI_addtail(&region_type.paneltypes, panel_type);
+ }
+
+ {
+ PanelType *panel_type = (PanelType *)MEM_callocN(sizeof(PanelType), __func__);
+ strcpy(panel_type->idname, "SPREADSHEET_PT_filter");
+ strcpy(panel_type->label, "");
+ strcpy(panel_type->category, "Filters");
+ strcpy(panel_type->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
+ panel_type->flag = PANEL_TYPE_INSTANCED | PANEL_TYPE_DRAW_BOX | PANEL_TYPE_HEADER_EXPAND;
+ panel_type->draw_header = spreadsheet_filter_panel_draw_header;
+ panel_type->draw = spreadsheet_filter_panel_draw;
+ panel_type->get_list_data_expand_flag = get_filter_expand_flag;
+ panel_type->set_list_data_expand_flag = set_filter_expand_flag;
+ panel_type->reorder = filter_reorder;
+ BLI_addtail(&region_type.paneltypes, panel_type);
+ }
+}
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.hh b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.hh
new file mode 100644
index 00000000000..e22178b63ea
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.hh
@@ -0,0 +1,21 @@
+/*
+ * 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
+
+struct ARegionType;
+
+void register_row_filter_panels(ARegionType &region_type);