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')
-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
16 files changed, 1052 insertions, 70 deletions
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);