Spreadsheet Editor: Row Filters

This patch adds support for filtering rows based on rules and values.
Filters will work for any attribute data source, they are a property
of the spreadsheet rather than of the attribute system. The properties
displayed in the row filter can depend on data type of the currently
visible column with that name. If the name is no longer visible, the
row filter filter is grayed out, but it will remember the value until
a column with its name is visible again.

Note: The comments in `screen.c` combined with tagging the sidebar
for redraw after the main region point to a lack of understanding
or technical debt, that is a point to improve in the future.

**Future Improvements**
* T89272: A search menu for visible columns when adding a new filter.
* T89273: Possibly a "Range" operation.

Differential Revision: https://developer.blender.org/D10959
This commit is contained in:
Hans Goudey 2021-06-18 16:33:02 -05:00
parent d52b7dbe26
commit f9aea19d98
Notes: blender-bot 2023-02-14 10:29:30 +01:00
Referenced by issue #89310, Industry Compatible keymap not working
Referenced by issue #89100, Curve to Mesh does not work when using Mesh to Curve node with profile input.
Referenced by issue #86133, Rules based row filtering for attribute spreadsheet
25 changed files with 1381 additions and 88 deletions

View File

@ -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),

View File

@ -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),

View File

@ -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,
)

View File

@ -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

View File

@ -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);

View File

@ -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. */
}
}

View File

@ -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);

View File

@ -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));
}

View File

@ -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);

View File

@ -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

View File

@ -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");
@ -75,6 +79,15 @@ static SpaceLink *spreadsheet_create(const ScrArea *UNUSED(area), const Scene *U
region->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_TOP : RGN_ALIGN_BOTTOM;
}
{
/* Properties region. */
ARegion *region = (ARegion *)MEM_callocN(sizeof(ARegion), "spreadsheet right region");
BLI_addtail(&spreadsheet_space->regionbase, region);
region->regiontype = RGN_TYPE_UI;
region->alignment = RGN_ALIGN_RIGHT;
region->flag = RGN_FLAG_HIDDEN;
}
{
/* Main window. */
ARegion *region = (ARegion *)MEM_callocN(sizeof(ARegion), "spreadsheet main region");
@ -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);
}

View File

@ -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);
}

View File

@ -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

View File

@ -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;
}

View File

@ -53,6 +53,15 @@ class DataSource {
return {};
}
/**
* 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.
*/

View File

@ -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]; },

View File

@ -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;

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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.

View File

@ -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;

View File

@ -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(