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:
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/blender_default.py17
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py18
-rw-r--r--release/scripts/startup/bl_ui/space_spreadsheet.py20
-rw-r--r--source/blender/blenkernel/BKE_blender_version.h2
-rw-r--r--source/blender/blenkernel/intern/screen.c20
-rw-r--r--source/blender/blenloader/intern/versioning_300.c57
-rw-r--r--source/blender/editors/include/ED_screen.h1
-rw-r--r--source/blender/editors/interface/interface_panel.c2
-rw-r--r--source/blender/editors/screen/screen_ops.c5
-rw-r--r--source/blender/editors/space_spreadsheet/CMakeLists.txt5
-rw-r--r--source/blender/editors/space_spreadsheet/space_spreadsheet.cc92
-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_data_source.hh9
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc122
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh3
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_ops.cc75
-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
-rw-r--r--source/blender/makesdna/DNA_space_types.h62
-rw-r--r--source/blender/makesrna/RNA_access.h2
-rw-r--r--source/blender/makesrna/intern/rna_space.c149
25 files changed, 1381 insertions, 88 deletions
diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
index a82304e7614..bd14b2c532c 100644
--- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py
+++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
@@ -3010,6 +3010,22 @@ def km_clip_dopesheet_editor(_params):
return keymap
+def km_spreadsheet_generic(_params):
+ items = []
+ keymap = (
+ "Spreadsheet Generic",
+ {"space_type": 'SPREADSHEET', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ *_template_space_region_type_toggle(
+ sidebar_key={"type": 'N', "value": 'PRESS'},
+ ),
+ ])
+
+ return keymap
+
# ------------------------------------------------------------------------------
# Animation
@@ -7067,6 +7083,7 @@ def generate_keymaps(params=None):
km_image(params),
km_node_generic(params),
km_node_editor(params),
+ km_spreadsheet_generic(params),
km_info(params),
km_file_browser(params),
km_file_browser_main(params),
diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
index e56783fcc21..237b7237c27 100644
--- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
+++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py
@@ -2144,6 +2144,23 @@ def km_clip_dopesheet_editor(_params):
return keymap
+def km_spreadsheet_generic(_params):
+ items = []
+ keymap = (
+ "Spreadsheet Generic",
+ {"space_type": 'SPREADSHEET', "region_type": 'WINDOW'},
+ {"items": items},
+ )
+
+ items.extend([
+ *_template_space_region_type_toggle(
+ sidebar_key={"type": 'N', "value": 'PRESS'},
+ ),
+ ])
+
+ return keymap
+
+
# ------------------------------------------------------------------------------
# Animation
@@ -4067,6 +4084,7 @@ def generate_keymaps_impl(params=None):
km_image(params),
km_node_generic(params),
km_node_editor(params),
+ km_spreadsheet_generic(params),
km_info(params),
km_file_browser(params),
km_file_browser_main(params),
diff --git a/release/scripts/startup/bl_ui/space_spreadsheet.py b/release/scripts/startup/bl_ui/space_spreadsheet.py
index 13e435a7350..360849a0c7a 100644
--- a/release/scripts/startup/bl_ui/space_spreadsheet.py
+++ b/release/scripts/startup/bl_ui/space_spreadsheet.py
@@ -59,9 +59,12 @@ class SPREADSHEET_HT_header(bpy.types.Header):
layout.operator("spreadsheet.toggle_pin", text="", icon=pin_icon, emboss=False)
layout.separator_spacer()
-
- if isinstance(obj, bpy.types.Object) and obj.mode == 'EDIT':
- layout.prop(space, "show_only_selected", text="Selected Only")
+
+ row = layout.row(align=True)
+ sub = row.row(align=True)
+ sub.active = self.selection_filter_available(space)
+ sub.prop(space, "show_only_selected", text="")
+ row.prop(space, "use_filter", toggle=True, icon='FILTER', icon_only=True)
def draw_without_context_path(self, layout):
layout.label(text="No active context")
@@ -102,6 +105,17 @@ class SPREADSHEET_HT_header(bpy.types.Header):
def draw_spreadsheet_context_path_icon(self, layout, space, icon='RIGHTARROW_THIN'):
layout.prop(space, "display_context_path_collapsed", icon_only=True, emboss=False, icon=icon)
+ def selection_filter_available(self, space):
+ root_context = space.context_path[0]
+ if root_context.type != 'OBJECT':
+ return False
+ obj = root_context.object
+ if obj is None:
+ return False
+ if obj.type != 'MESH' or obj.mode != 'EDIT':
+ return False
+ return True
+
classes = (
SPREADSHEET_HT_header,
)
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h
index 6ad910ff8ab..1767077fa45 100644
--- a/source/blender/blenkernel/BKE_blender_version.h
+++ b/source/blender/blenkernel/BKE_blender_version.h
@@ -39,7 +39,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
-#define BLENDER_FILE_SUBVERSION 3
+#define BLENDER_FILE_SUBVERSION 4
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and show a warning if the file
diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c
index 2d59e978a81..269aeaebe82 100644
--- a/source/blender/blenkernel/intern/screen.c
+++ b/source/blender/blenkernel/intern/screen.c
@@ -1357,12 +1357,21 @@ static void write_area(BlendWriter *writer, ScrArea *area)
}
else if (sl->spacetype == SPACE_SPREADSHEET) {
BLO_write_struct(writer, SpaceSpreadsheet, sl);
-
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
+
+ LISTBASE_FOREACH (SpreadsheetRowFilter *, row_filter, &sspreadsheet->row_filters) {
+ BLO_write_struct(writer, SpreadsheetRowFilter, row_filter);
+ BLO_write_string(writer, row_filter->value_string);
+ }
+
LISTBASE_FOREACH (SpreadsheetColumn *, column, &sspreadsheet->columns) {
BLO_write_struct(writer, SpreadsheetColumn, column);
BLO_write_struct(writer, SpreadsheetColumnID, column->id);
BLO_write_string(writer, column->id->name);
+ /* While the display name is technically runtime data, we write it here, otherwise the row
+ * filters might not now their type if their region draws before the main region.
+ * This would ideally be cleared here. */
+ BLO_write_string(writer, column->display_name);
}
LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) {
switch (context->type) {
@@ -1743,11 +1752,18 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area)
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
sspreadsheet->runtime = NULL;
-
+ BLO_read_list(reader, &sspreadsheet->row_filters);
+ LISTBASE_FOREACH (SpreadsheetRowFilter *, row_filter, &sspreadsheet->row_filters) {
+ BLO_read_data_address(reader, &row_filter->value_string);
+ }
BLO_read_list(reader, &sspreadsheet->columns);
LISTBASE_FOREACH (SpreadsheetColumn *, column, &sspreadsheet->columns) {
BLO_read_data_address(reader, &column->id);
BLO_read_data_address(reader, &column->id->name);
+ /* While the display name is technically runtime data, it is loaded here, otherwise the row
+ * filters might not now their type if their region draws before the main region.
+ * This would ideally be cleared here. */
+ BLO_read_data_address(reader, &column->display_name);
}
BLO_read_list(reader, &sspreadsheet->context_path);
diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c
index bdefe51f635..0fe1267b871 100644
--- a/source/blender/blenloader/intern/versioning_300.c
+++ b/source/blender/blenloader/intern/versioning_300.c
@@ -44,6 +44,8 @@
#include "MEM_guardedalloc.h"
+#include "versioning_common.h"
+
static void sort_linked_ids(Main *bmain)
{
ListBase *lb;
@@ -310,17 +312,37 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
- /**
- * Versioning code until next subversion bump goes here.
- *
- * \note Be sure to check when bumping the version:
- * - "versioning_userdef.c", #blo_do_versions_userdef
- * - "versioning_userdef.c", #do_versions_theme
- *
- * \note Keep this message at the bottom of the function.
- */
- {
- /* Keep this block, even when empty. */
+ if (!MAIN_VERSION_ATLEAST(bmain, 300, 4)) {
+ /* Add a properties sidebar to the spreadsheet editor. */
+ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
+ if (sl->spacetype == SPACE_SPREADSHEET) {
+ ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase :
+ &sl->regionbase;
+ ARegion *new_sidebar = do_versions_add_region_if_not_found(
+ regionbase, RGN_TYPE_UI, "sidebar for spreadsheet", RGN_TYPE_FOOTER);
+ if (new_sidebar != NULL) {
+ new_sidebar->alignment = RGN_ALIGN_RIGHT;
+ new_sidebar->flag |= RGN_FLAG_HIDDEN;
+ }
+ }
+ }
+ }
+ }
+
+ /* Enable spreadsheet filtering in old files without row filters. */
+ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
+ if (sl->spacetype == SPACE_SPREADSHEET) {
+ SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
+ sspreadsheet->filter_flag |= SPREADSHEET_FILTER_ENABLE;
+ }
+ }
+ }
+ }
+
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type == NTREE_GEOMETRY) {
version_node_socket_name(ntree, GEO_NODE_BOUNDING_BOX, "Mesh", "Bounding Box");
@@ -369,4 +391,17 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
BKE_animdata_main_cb(bmain, do_version_bbone_len_scale_animdata_cb, NULL);
}
}
+
+ /**
+ * Versioning code until next subversion bump goes here.
+ *
+ * \note Be sure to check when bumping the version:
+ * - "versioning_userdef.c", #blo_do_versions_userdef
+ * - "versioning_userdef.c", #do_versions_theme
+ *
+ * \note Keep this message at the bottom of the function.
+ */
+ {
+ /* Keep this block, even when empty. */
+ }
}
diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h
index a611fb50e4e..823050b46f7 100644
--- a/source/blender/editors/include/ED_screen.h
+++ b/source/blender/editors/include/ED_screen.h
@@ -317,6 +317,7 @@ bool ED_operator_animview_active(struct bContext *C);
bool ED_operator_outliner_active(struct bContext *C);
bool ED_operator_outliner_active_no_editobject(struct bContext *C);
bool ED_operator_file_active(struct bContext *C);
+bool ED_operator_spreadsheet_active(struct bContext *C);
bool ED_operator_action_active(struct bContext *C);
bool ED_operator_buttons_active(struct bContext *C);
bool ED_operator_node_active(struct bContext *C);
diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c
index 6505a7cd76a..6694535e3af 100644
--- a/source/blender/editors/interface/interface_panel.c
+++ b/source/blender/editors/interface/interface_panel.c
@@ -674,7 +674,7 @@ static bool panel_type_context_poll(ARegion *region,
const PanelType *panel_type,
const char *context)
{
- if (UI_panel_category_is_visible(region)) {
+ if (!BLI_listbase_is_empty(&region->panels_category)) {
return STREQ(panel_type->category, UI_panel_category_active_get(region, false));
}
diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c
index 88227207a24..f0e12ca60e9 100644
--- a/source/blender/editors/screen/screen_ops.c
+++ b/source/blender/editors/screen/screen_ops.c
@@ -279,6 +279,11 @@ bool ED_operator_file_active(bContext *C)
return ed_spacetype_test(C, SPACE_FILE);
}
+bool ED_operator_spreadsheet_active(bContext *C)
+{
+ return ed_spacetype_test(C, SPACE_SPREADSHEET);
+}
+
bool ED_operator_action_active(bContext *C)
{
return ed_spacetype_test(C, SPACE_ACTION);
diff --git a/source/blender/editors/space_spreadsheet/CMakeLists.txt b/source/blender/editors/space_spreadsheet/CMakeLists.txt
index b2a0905d4a2..7e3f3db9bc8 100644
--- a/source/blender/editors/space_spreadsheet/CMakeLists.txt
+++ b/source/blender/editors/space_spreadsheet/CMakeLists.txt
@@ -20,6 +20,7 @@ set(INC
../../blenfont
../../blenkernel
../../blenlib
+ ../../blentranslation
../../bmesh
../../depsgraph
../../functions
@@ -40,6 +41,8 @@ set(SRC
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
@@ -50,6 +53,8 @@ set(SRC
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..8c42f28b5f4 100644
--- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
+++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
@@ -49,6 +49,8 @@
#include "spreadsheet_data_source_geometry.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 +61,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 +80,15 @@ static SpaceLink *spreadsheet_create(const ScrArea *UNUSED(area), const Scene *U
}
{
+ /* 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 +101,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 +130,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 +150,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,8 +184,15 @@ 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)
@@ -346,24 +377,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 +393,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)
@@ -511,6 +534,24 @@ static void spreadsheet_footer_region_listener(const wmRegionListenerParams *UNU
{
}
+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 +604,20 @@ 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);
+
BKE_spacetype_register(st);
}
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_data_source.hh b/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh
index de47109a144..fad1770e621 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 iff 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 452885959f6..77248254d7b 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
@@ -69,28 +69,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 +109,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,6 +121,7 @@ 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) {
@@ -137,55 +146,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 +210,40 @@ 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
+bool GeometryDataSource::has_selection_filter() const
+{
+ Object *object_orig = DEG_get_original_object(object_eval_);
+ if (object_orig->type == OB_MESH) {
+ if (object_orig->mode == OB_MODE_EDIT) {
+ return true;
+ }
+ }
+ return false;
+}
+
+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 +264,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 +272,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 +304,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 +331,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 +341,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 +351,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 +363,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]; },
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_ops.cc b/source/blender/editors/space_spreadsheet/spreadsheet_ops.cc
index 770bd207e8d..fcbc37346e6 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_ops.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_ops.cc
@@ -14,8 +14,83 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#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);
+}
void spreadsheet_operatortypes()
{
+ WM_operatortype_append(SPREADSHEET_OT_add_row_filter_rule);
+ WM_operatortype_append(SPREADSHEET_OT_remove_row_filter_rule);
}
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..4835a73b06b
--- /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 *column);
+
+} // 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..dbd2ef157af
--- /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 = (Panel *)region->panels.first;
+ LISTBASE_FOREACH (SpreadsheetRowFilter *, row_filter, row_filters) {
+
+ /* Move to the next instanced panel corresponding to the next filter. */
+ while ((panel->type == nullptr) || !(panel->type->flag & PANEL_TYPE_INSTANCED)) {
+ panel = panel->next;
+ BLI_assert(panel != 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, filter_ptr);
+
+ panel = panel->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);
diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h
index 71764d9308c..7804ece9769 100644
--- a/source/blender/makesdna/DNA_space_types.h
+++ b/source/blender/makesdna/DNA_space_types.h
@@ -1874,6 +1874,19 @@ typedef struct SpreadsheetColumn {
* #SpreadsheetColumnID in the future for different kinds of ids.
*/
SpreadsheetColumnID *id;
+
+ /**
+ * An indicator of the type of values in the column, set at runtime.
+ * #eSpreadsheetColumnValueType.
+ */
+ uint8_t data_type;
+ char _pad0[7];
+
+ /**
+ * The final column name generated by the data source, also just
+ * cached at runtime when the data source columns are generated.
+ */
+ char *display_name;
} SpreadsheetColumn;
/**
@@ -1914,6 +1927,9 @@ typedef struct SpaceSpreadsheet {
/* List of #SpreadsheetColumn. */
ListBase columns;
+ /* SpreadsheetRowFilter. */
+ ListBase row_filters;
+
/**
* List of #SpreadsheetContext.
* This is a path to the data that is displayed in the spreadsheet.
@@ -1945,8 +1961,44 @@ typedef enum eSpaceSpreadsheet_Flag {
typedef enum eSpaceSpreadsheet_FilterFlag {
SPREADSHEET_FILTER_SELECTED_ONLY = (1 << 0),
+ SPREADSHEET_FILTER_ENABLE = (1 << 1),
} eSpaceSpreadsheet_FilterFlag;
+typedef struct SpreadsheetRowFilter {
+ struct SpreadsheetRowFilter *next, *prev;
+
+ char column_name[64]; /* MAX_NAME. */
+
+ /* eSpreadsheetFilterOperation. */
+ uint8_t operation;
+ /* eSpaceSpreadsheet_RowFilterFlag. */
+ uint8_t flag;
+
+ char _pad0[2];
+
+ int value_int;
+ char *value_string;
+ float value_float;
+ float threshold;
+ float value_float2[2];
+ float value_float3[3];
+ float value_color[4];
+
+ char _pad1[4];
+} SpreadsheetRowFilter;
+
+typedef enum eSpaceSpreadsheet_RowFilterFlag {
+ SPREADSHEET_ROW_FILTER_UI_EXPAND = (1 << 0),
+ SPREADSHEET_ROW_FILTER_BOOL_VALUE = (1 << 1),
+ SPREADSHEET_ROW_FILTER_ENABLED = (1 << 2),
+} eSpaceSpreadsheet_RowFilterFlag;
+
+typedef enum eSpreadsheetFilterOperation {
+ SPREADSHEET_ROW_FILTER_EQUAL = 0,
+ SPREADSHEET_ROW_FILTER_GREATER = 1,
+ SPREADSHEET_ROW_FILTER_LESS = 2,
+} eSpreadsheetFilterOperation;
+
typedef enum eSpaceSpreadsheet_ObjectEvalState {
SPREADSHEET_OBJECT_EVAL_STATE_EVALUATED = 0,
SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL = 1,
@@ -1958,6 +2010,16 @@ typedef enum eSpaceSpreadsheet_ContextType {
SPREADSHEET_CONTEXT_NODE = 2,
} eSpaceSpreadsheet_ContextType;
+typedef enum eSpreadsheetColumnValueType {
+ SPREADSHEET_VALUE_TYPE_BOOL = 0,
+ SPREADSHEET_VALUE_TYPE_INT32 = 1,
+ SPREADSHEET_VALUE_TYPE_FLOAT = 2,
+ SPREADSHEET_VALUE_TYPE_FLOAT2 = 3,
+ SPREADSHEET_VALUE_TYPE_FLOAT3 = 4,
+ SPREADSHEET_VALUE_TYPE_COLOR = 5,
+ SPREADSHEET_VALUE_TYPE_INSTANCES = 6,
+} eSpreadsheetColumnValueType;
+
/**
* We can't just use UI_UNIT_X, because it does not take `widget.points` into account, which
* modifies the width of text as well.
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index 379e3e77b6e..c691eb3b534 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -615,10 +615,12 @@ extern StructRNA RNA_Spline;
extern StructRNA RNA_SplineIKConstraint;
extern StructRNA RNA_SplinePoint;
extern StructRNA RNA_SpotLight;
+extern StructRNA RNA_SpreadsheetColumnID;
extern StructRNA RNA_SpreadsheetContext;
extern StructRNA RNA_SpreadsheetContextObject;
extern StructRNA RNA_SpreadsheetContextModifier;
extern StructRNA RNA_SpreadsheetContextNode;
+extern StructRNA RNA_SpreadsheetRowFilter;
extern StructRNA RNA_Stereo3dDisplay;
extern StructRNA RNA_StretchToConstraint;
extern StructRNA RNA_StringAttribute;
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index 0af6f85112e..37e2664fa79 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -7411,6 +7411,131 @@ static void rna_def_space_clip(BlenderRNA *brna)
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_CLIP, NULL);
}
+static void rna_def_spreadsheet_column_id(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "SpreadsheetColumnID", NULL);
+ RNA_def_struct_sdna(srna, "SpreadsheetColumnID");
+ RNA_def_struct_ui_text(
+ srna, "Spreadsheet Column ID", "Data used to identify a spreadsheet column");
+
+ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Column Name", "");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
+}
+
+static void rna_def_spreadsheet_column(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ static const EnumPropertyItem data_type_items[] = {
+ {SPREADSHEET_VALUE_TYPE_INT32, "INT32", ICON_NONE, "Integer", ""},
+ {SPREADSHEET_VALUE_TYPE_FLOAT, "FLOAT", ICON_NONE, "Float", ""},
+ {SPREADSHEET_VALUE_TYPE_BOOL, "BOOLEAN", ICON_NONE, "Boolean", ""},
+ {SPREADSHEET_VALUE_TYPE_INSTANCES, "INSTANCES", ICON_NONE, "Instances", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ srna = RNA_def_struct(brna, "SpreadsheetColumn", NULL);
+ RNA_def_struct_sdna(srna, "SpreadsheetColumn");
+ RNA_def_struct_ui_text(
+ srna, "Spreadsheet Column", "Persistent data associated with a spreadsheet column");
+
+ prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "data_type");
+ RNA_def_property_enum_items(prop, data_type_items);
+ RNA_def_property_ui_text(
+ prop, "Data Type", "The data type of the corresponding column visible in the spreadsheet");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
+
+ rna_def_spreadsheet_column_id(brna);
+
+ prop = RNA_def_property(srna, "id", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "SpreadsheetColumnID");
+ RNA_def_property_ui_text(
+ prop, "ID", "Data used to identify the corresponding data from the data source");
+}
+
+static void rna_def_spreadsheet_row_filter(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ static const EnumPropertyItem rule_operation_items[] = {
+ {SPREADSHEET_ROW_FILTER_EQUAL, "EQUAL", ICON_NONE, "Equal To", ""},
+ {SPREADSHEET_ROW_FILTER_GREATER, "GREATER", ICON_NONE, "Greater Than", ""},
+ {SPREADSHEET_ROW_FILTER_LESS, "LESS", ICON_NONE, "Less Than", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ srna = RNA_def_struct(brna, "SpreadsheetRowFilter", NULL);
+ RNA_def_struct_sdna(srna, "SpreadsheetRowFilter");
+ RNA_def_struct_ui_text(srna, "Spreadsheet Row Filter", "");
+
+ prop = RNA_def_property(srna, "enabled", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SPREADSHEET_ROW_FILTER_ENABLED);
+ RNA_def_property_ui_text(prop, "Enabled", "");
+ RNA_def_property_ui_icon(prop, ICON_CHECKBOX_DEHLT, 1);
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
+
+ prop = RNA_def_property(srna, "show_expanded", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SPREADSHEET_ROW_FILTER_UI_EXPAND);
+ RNA_def_property_ui_text(prop, "Show Expanded", "");
+ RNA_def_property_ui_icon(prop, ICON_DISCLOSURE_TRI_RIGHT, 1);
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
+
+ prop = RNA_def_property(srna, "column_name", PROP_STRING, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Column Name", "");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
+
+ prop = RNA_def_property(srna, "operation", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rule_operation_items);
+ RNA_def_property_ui_text(prop, "Operation", "");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
+
+ prop = RNA_def_property(srna, "value_float", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Float Value", "");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
+
+ prop = RNA_def_property(srna, "value_float2", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_array(prop, 2);
+ RNA_def_property_ui_text(prop, "2D Vector Value", "");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
+
+ prop = RNA_def_property(srna, "value_float3", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_array(prop, 3);
+ RNA_def_property_ui_text(prop, "Vector Value", "");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
+
+ prop = RNA_def_property(srna, "value_color", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_array(prop, 4);
+ RNA_def_property_ui_text(prop, "Color Value", "");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
+
+ prop = RNA_def_property(srna, "value_string", PROP_STRING, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Text Value", "");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
+
+ prop = RNA_def_property(srna, "threshold", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Threshold", "How close float values need to be to be equal");
+ RNA_def_property_range(prop, 0.0, FLT_MAX);
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
+
+ prop = RNA_def_property(srna, "value_int", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "value_int");
+ RNA_def_property_ui_text(prop, "Integer Value", "");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
+
+ prop = RNA_def_property(srna, "value_boolean", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SPREADSHEET_ROW_FILTER_BOOL_VALUE);
+ RNA_def_property_ui_text(prop, "Boolean Value", "");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
+}
+
static const EnumPropertyItem spreadsheet_context_type_items[] = {
{SPREADSHEET_CONTEXT_OBJECT, "OBJECT", ICON_NONE, "Object", ""},
{SPREADSHEET_CONTEXT_MODIFIER, "MODIFIER", ICON_NONE, "Modifier", ""},
@@ -7545,13 +7670,18 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna)
srna = RNA_def_struct(brna, "SpaceSpreadsheet", "Space");
RNA_def_struct_ui_text(srna, "Space Spreadsheet", "Spreadsheet space data");
- rna_def_space_generic_show_region_toggles(srna, (1 << RGN_TYPE_FOOTER));
+ rna_def_space_generic_show_region_toggles(srna, (1 << RGN_TYPE_UI) | (1 << RGN_TYPE_FOOTER));
prop = RNA_def_property(srna, "is_pinned", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", SPREADSHEET_FLAG_PINNED);
RNA_def_property_ui_text(prop, "Is Pinned", "Context path is pinned");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
+ prop = RNA_def_property(srna, "use_filter", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "filter_flag", SPREADSHEET_FILTER_ENABLE);
+ RNA_def_property_ui_text(prop, "Use Filter", "");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
+
prop = RNA_def_property(srna, "display_context_path_collapsed", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", SPREADSHEET_FLAG_CONTEXT_PATH_COLLAPSED);
RNA_def_property_ui_text(prop, "Display Context Path Collapsed", "");
@@ -7566,6 +7696,7 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "filter_flag", SPREADSHEET_FILTER_SELECTED_ONLY);
RNA_def_property_ui_text(
prop, "Show Only Selected", "Only include rows that correspond to selected elements");
+ RNA_def_property_ui_icon(prop, ICON_RESTRICT_SELECT_OFF, 0);
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
prop = RNA_def_property(srna, "geometry_component_type", PROP_ENUM, PROP_NONE);
@@ -7587,6 +7718,22 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Object Evaluation State", "");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
+ rna_def_spreadsheet_column(brna);
+
+ prop = RNA_def_property(srna, "columns", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "columns", NULL);
+ RNA_def_property_struct_type(prop, "SpreadsheetColumn");
+ RNA_def_property_ui_text(prop, "Columns", "Persistent data associated with spreadsheet columns");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
+
+ rna_def_spreadsheet_row_filter(brna);
+
+ prop = RNA_def_property(srna, "row_filters", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "row_filters", NULL);
+ RNA_def_property_struct_type(prop, "SpreadsheetRowFilter");
+ RNA_def_property_ui_text(prop, "Row Filters", "Filters to remove rows from the displayed data");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
+
func = RNA_def_function(
srna, "set_geometry_node_context", "rna_spreadsheet_set_geometry_node_context");
RNA_def_function_ui_description(