Blender V4.5
spreadsheet_row_filter_ui.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include <cstring>
6
7#include "BLI_listbase.h"
8#include "BLI_string.h"
9#include "BLI_string_ref.hh"
10
11#include "DNA_screen_types.h"
12#include "DNA_space_types.h"
13
14#include "BKE_screen.hh"
15
16#include "RNA_access.hh"
17#include "RNA_prototypes.hh"
18
19#include "UI_interface.hh"
20#include "UI_resources.hh"
21
22#include "BLT_translation.hh"
23
24#include "WM_api.hh"
25#include "WM_types.hh"
26
27#include "ED_spreadsheet.hh"
28
29#include "spreadsheet_intern.hh"
31
32#include <sstream>
33
35
36static void filter_panel_id_fn(void * /*row_filter_v*/, char *r_name)
37{
38 /* All row filters use the same panel ID. */
39 BLI_strncpy(r_name, "SPREADSHEET_PT_filter", BKE_ST_MAXNAME);
40}
41
42static std::string operation_string(const eSpreadsheetColumnValueType data_type,
43 const eSpreadsheetFilterOperation operation)
44{
46 return "=";
47 }
48
49 switch (operation) {
51 return "=";
53 return ">";
55 return "<";
56 }
58 return "";
59}
60
61static std::string value_string(const SpreadsheetRowFilter &row_filter,
62 const eSpreadsheetColumnValueType data_type)
63{
64 switch (data_type) {
67 return std::to_string(row_filter.value_int);
69 std::ostringstream result;
70 result.precision(3);
71 result << std::fixed << row_filter.value_float;
72 return result.str();
73 }
75 std::ostringstream result;
76 result << "(" << row_filter.value_int2[0] << ", " << row_filter.value_int2[1] << ")";
77 return result.str();
78 }
80 std::ostringstream result;
81 result.precision(3);
82 result << std::fixed << "(" << row_filter.value_float2[0] << ", "
83 << row_filter.value_float2[1] << ")";
84 return result.str();
85 }
87 std::ostringstream result;
88 result.precision(3);
89 result << std::fixed << "(" << row_filter.value_float3[0] << ", "
90 << row_filter.value_float3[1] << ", " << row_filter.value_float3[2] << ")";
91 return result.str();
92 }
94 return (row_filter.flag & SPREADSHEET_ROW_FILTER_BOOL_VALUE) ? IFACE_("True") :
95 IFACE_("False");
97 if (row_filter.value_string != nullptr) {
98 return row_filter.value_string;
99 }
100 return "";
103 std::ostringstream result;
104 result.precision(3);
105 result << std::fixed << "(" << row_filter.value_color[0] << ", " << row_filter.value_color[1]
106 << ", " << row_filter.value_color[2] << ", " << row_filter.value_color[3] << ")";
107 return result.str();
108 }
110 return row_filter.value_string;
114 return "";
115 }
117 return "";
118}
119
121 const SpaceSpreadsheet &sspreadsheet, const StringRef column_name)
122{
123 const SpreadsheetTable *table = get_active_table(sspreadsheet);
124 if (!table) {
125 return nullptr;
126 }
127 for (const SpreadsheetColumn *column : Span{table->columns, table->num_columns}) {
128 if (column->display_name == column_name) {
129 return column;
130 }
131 }
132 return nullptr;
133}
134
136{
137 uiLayout *layout = panel->layout;
139 PointerRNA *filter_ptr = UI_panel_custom_data_get(panel);
141 const StringRef column_name = filter->column_name;
143
144 const SpreadsheetColumn *column = lookup_visible_column_for_filter(*sspreadsheet, column_name);
145 if (!(sspreadsheet->filter_flag & SPREADSHEET_FILTER_ENABLE) ||
146 (column == nullptr && !column_name.is_empty()))
147 {
148 uiLayoutSetActive(layout, false);
149 }
150
151 uiLayout *row = &layout->row(true);
153 row->prop(filter_ptr, "enabled", UI_ITEM_R_ICON_ONLY, "", ICON_NONE);
154
155 if (column_name.is_empty()) {
156 row->label(IFACE_("Filter"), ICON_NONE);
157 }
158 else if (column == nullptr) {
159 row->label(column_name.data(), ICON_NONE);
160 }
161 else {
163 std::stringstream ss;
164 ss << column_name;
165 ss << " ";
166 ss << operation_string(data_type, operation);
167 ss << " ";
168 ss << value_string(*filter, data_type);
169 row->label(ss.str(), ICON_NONE);
170 }
171
172 row = &layout->row(true);
174 const int current_index = BLI_findindex(&sspreadsheet->row_filters, filter);
175 PointerRNA op_ptr = row->op("SPREADSHEET_OT_remove_row_filter_rule", "", ICON_X);
176 RNA_int_set(&op_ptr, "index", current_index);
177 /* Some padding so the X isn't too close to the drag icon. */
178 layout->separator(0.25f);
179}
180
182{
183 uiLayout *layout = panel->layout;
185 PointerRNA *filter_ptr = UI_panel_custom_data_get(panel);
187 const StringRef column_name = filter->column_name;
189
190 const SpreadsheetColumn *column = lookup_visible_column_for_filter(*sspreadsheet, column_name);
191 if (!(sspreadsheet->filter_flag & SPREADSHEET_FILTER_ENABLE) ||
193 (column == nullptr && !column_name.is_empty()))
194 {
195 uiLayoutSetActive(layout, false);
196 }
197
198 uiLayoutSetPropSep(layout, true);
199 uiLayoutSetPropDecorate(layout, false);
200
201 layout->prop(filter_ptr, "column_name", UI_ITEM_NONE, IFACE_("Column"), ICON_NONE);
202
203 /* Don't draw settings for filters with no corresponding visible column. */
204 if (column == nullptr || column_name.is_empty()) {
205 return;
206 }
207
208 switch (static_cast<eSpreadsheetColumnValueType>(column->data_type)) {
210 layout->prop(filter_ptr, "operation", UI_ITEM_NONE, std::nullopt, ICON_NONE);
211 layout->prop(filter_ptr, "value_int8", UI_ITEM_NONE, IFACE_("Value"), ICON_NONE);
212 break;
214 layout->prop(filter_ptr, "operation", UI_ITEM_NONE, std::nullopt, ICON_NONE);
215 layout->prop(filter_ptr, "value_int", UI_ITEM_NONE, IFACE_("Value"), ICON_NONE);
216 break;
218 layout->prop(filter_ptr, "operation", UI_ITEM_NONE, std::nullopt, ICON_NONE);
219 layout->prop(filter_ptr, "value_int2", UI_ITEM_NONE, IFACE_("Value"), ICON_NONE);
220 break;
222 layout->prop(filter_ptr, "operation", UI_ITEM_NONE, std::nullopt, ICON_NONE);
223 layout->prop(filter_ptr, "value_float", UI_ITEM_NONE, IFACE_("Value"), ICON_NONE);
224 if (operation == SPREADSHEET_ROW_FILTER_EQUAL) {
225 layout->prop(filter_ptr, "threshold", UI_ITEM_NONE, std::nullopt, ICON_NONE);
226 }
227 break;
229 layout->prop(filter_ptr, "operation", UI_ITEM_NONE, std::nullopt, ICON_NONE);
230 layout->prop(filter_ptr, "value_float2", UI_ITEM_NONE, IFACE_("Value"), ICON_NONE);
231 if (operation == SPREADSHEET_ROW_FILTER_EQUAL) {
232 layout->prop(filter_ptr, "threshold", UI_ITEM_NONE, std::nullopt, ICON_NONE);
233 }
234 break;
236 layout->prop(filter_ptr, "operation", UI_ITEM_NONE, std::nullopt, ICON_NONE);
237 layout->prop(filter_ptr, "value_float3", UI_ITEM_NONE, IFACE_("Value"), ICON_NONE);
238 if (operation == SPREADSHEET_ROW_FILTER_EQUAL) {
239 layout->prop(filter_ptr, "threshold", UI_ITEM_NONE, std::nullopt, ICON_NONE);
240 }
241 break;
243 layout->prop(filter_ptr, "value_boolean", UI_ITEM_NONE, IFACE_("Value"), ICON_NONE);
244 break;
246 layout->prop(filter_ptr, "value_string", UI_ITEM_NONE, IFACE_("Value"), ICON_NONE);
247 break;
250 layout->prop(filter_ptr, "operation", UI_ITEM_NONE, std::nullopt, ICON_NONE);
251 layout->prop(filter_ptr, "value_color", UI_ITEM_NONE, IFACE_("Value"), ICON_NONE);
252 if (operation == SPREADSHEET_ROW_FILTER_EQUAL) {
253 layout->prop(filter_ptr, "threshold", UI_ITEM_NONE, std::nullopt, ICON_NONE);
254 }
255 break;
257 layout->prop(filter_ptr, "value_string", UI_ITEM_NONE, IFACE_("Value"), ICON_NONE);
258 break;
262 layout->label(IFACE_("Unsupported column type"), ICON_ERROR);
263 break;
264 }
265}
266
268{
269 uiLayout *layout = panel->layout;
270 ARegion *region = CTX_wm_region(C);
271 bScreen *screen = CTX_wm_screen(C);
273 ListBase *row_filters = &sspreadsheet->row_filters;
274
275 if (!(sspreadsheet->filter_flag & SPREADSHEET_FILTER_ENABLE)) {
276 uiLayoutSetActive(layout, false);
277 }
278
279 layout->op("SPREADSHEET_OT_add_row_filter_rule", std::nullopt, ICON_ADD);
280
281 const bool panels_match = UI_panel_list_matches_data(region, row_filters, filter_panel_id_fn);
282
283 if (!panels_match) {
285 LISTBASE_FOREACH (SpreadsheetRowFilter *, row_filter, row_filters) {
286 char panel_idname[MAX_NAME];
287 filter_panel_id_fn(row_filter, panel_idname);
288
289 PointerRNA *filter_ptr = MEM_new<PointerRNA>("panel customdata");
290 *filter_ptr = RNA_pointer_create_discrete(
291 &screen->id, &RNA_SpreadsheetRowFilter, row_filter);
292
293 UI_panel_add_instanced(C, region, &region->panels, panel_idname, filter_ptr);
294 }
295 }
296 else {
297 /* Assuming there's only one group of instanced panels, update the custom data pointers. */
298 Panel *panel_iter = (Panel *)region->panels.first;
299 LISTBASE_FOREACH (SpreadsheetRowFilter *, row_filter, row_filters) {
300
301 /* Move to the next instanced panel corresponding to the next filter. */
302 while ((panel_iter->type == nullptr) || !(panel_iter->type->flag & PANEL_TYPE_INSTANCED)) {
303 panel_iter = panel_iter->next;
304 BLI_assert(panel_iter != nullptr); /* There shouldn't be fewer panels than filters. */
305 }
306
307 PointerRNA *filter_ptr = MEM_new<PointerRNA>("panel customdata");
308 *filter_ptr = RNA_pointer_create_discrete(
309 &screen->id, &RNA_SpreadsheetRowFilter, row_filter);
310 UI_panel_custom_data_set(panel_iter, filter_ptr);
311
312 panel_iter = panel_iter->next;
313 }
314 }
315}
316
317static void filter_reorder(bContext *C, Panel *panel, int new_index)
318{
320 ListBase *row_filters = &sspreadsheet->row_filters;
321 PointerRNA *filter_ptr = UI_panel_custom_data_get(panel);
323
324 int current_index = BLI_findindex(row_filters, filter);
325 BLI_assert(current_index >= 0);
326 BLI_assert(new_index >= 0);
327
328 BLI_listbase_link_move(row_filters, filter, new_index - current_index);
329}
330
331static short get_filter_expand_flag(const bContext * /*C*/, Panel *panel)
332{
333 PointerRNA *filter_ptr = UI_panel_custom_data_get(panel);
335
336 return short(filter->flag) & SPREADSHEET_ROW_FILTER_UI_EXPAND;
337}
338
339static void set_filter_expand_flag(const bContext * /*C*/, Panel *panel, short expand_flag)
340{
341 PointerRNA *filter_ptr = UI_panel_custom_data_get(panel);
343
347}
348
350{
351 {
352 PanelType *panel_type = MEM_callocN<PanelType>(__func__);
353 STRNCPY(panel_type->idname, "SPREADSHEET_PT_row_filters");
354 STRNCPY(panel_type->label, N_("Filters"));
355 STRNCPY(panel_type->category, "Filters");
357 panel_type->flag = PANEL_TYPE_NO_HEADER;
359 BLI_addtail(&region_type.paneltypes, panel_type);
360 }
361
362 {
363 PanelType *panel_type = MEM_callocN<PanelType>(__func__);
364 STRNCPY(panel_type->idname, "SPREADSHEET_PT_filter");
365 STRNCPY(panel_type->label, "");
366 STRNCPY(panel_type->category, "Filters");
373 panel_type->reorder = filter_reorder;
374 BLI_addtail(&region_type.paneltypes, panel_type);
375 }
376}
377
378} // namespace blender::ed::spreadsheet
bScreen * CTX_wm_screen(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
SpaceSpreadsheet * CTX_wm_space_spreadsheet(const bContext *C)
#define BKE_ST_MAXNAME
Definition BKE_screen.hh:72
@ PANEL_TYPE_NO_HEADER
@ PANEL_TYPE_INSTANCED
@ PANEL_TYPE_HEADER_EXPAND
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
int BLI_findindex(const ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:586
#define LISTBASE_FOREACH(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void void void bool BLI_listbase_link_move(ListBase *listbase, void *vlink, int step) ATTR_NONNULL()
Definition listbase.cc:436
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:688
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
#define SET_FLAG_FROM_TEST(value, test, flag)
#define ELEM(...)
#define IFACE_(msgid)
#define BLT_I18NCONTEXT_DEFAULT_BPYRNA
@ SPREADSHEET_ROW_FILTER_BOOL_VALUE
@ SPREADSHEET_ROW_FILTER_UI_EXPAND
@ SPREADSHEET_ROW_FILTER_ENABLED
@ SPREADSHEET_FILTER_ENABLE
eSpreadsheetFilterOperation
@ SPREADSHEET_ROW_FILTER_GREATER
@ SPREADSHEET_ROW_FILTER_EQUAL
@ SPREADSHEET_ROW_FILTER_LESS
eSpreadsheetColumnValueType
@ SPREADSHEET_VALUE_TYPE_INT8
@ SPREADSHEET_VALUE_TYPE_FLOAT
@ SPREADSHEET_VALUE_TYPE_INT32_2D
@ SPREADSHEET_VALUE_TYPE_BYTE_COLOR
@ SPREADSHEET_VALUE_TYPE_UNKNOWN
@ SPREADSHEET_VALUE_TYPE_FLOAT3
@ SPREADSHEET_VALUE_TYPE_BOOL
@ SPREADSHEET_VALUE_TYPE_STRING
@ SPREADSHEET_VALUE_TYPE_QUATERNION
@ SPREADSHEET_VALUE_TYPE_FLOAT4X4
@ SPREADSHEET_VALUE_TYPE_INT32
@ SPREADSHEET_VALUE_TYPE_FLOAT2
@ SPREADSHEET_VALUE_TYPE_COLOR
@ SPREADSHEET_VALUE_TYPE_INSTANCES
#define C
Definition RandGen.cpp:29
void UI_panels_free_instanced(const bContext *C, ARegion *region)
void UI_panel_custom_data_set(Panel *panel, PointerRNA *custom_data)
bool UI_panel_list_matches_data(ARegion *region, ListBase *data, uiListPanelIDFromDataFunc panel_idname_func)
PointerRNA * UI_panel_custom_data_get(const Panel *panel)
Panel * UI_panel_add_instanced(const bContext *C, ARegion *region, ListBase *panels, const char *panel_idname, PointerRNA *custom_data)
@ UI_ITEM_R_ICON_ONLY
void uiLayoutSetActive(uiLayout *layout, bool active)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
#define UI_ITEM_NONE
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
void uiLayoutSetEmboss(uiLayout *layout, blender::ui::EmbossType emboss)
constexpr bool is_empty() const
constexpr const char * data() const
#define filter
#define MAX_NAME
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
static void spreadsheet_filter_panel_draw(const bContext *C, Panel *panel)
static std::string operation_string(const eSpreadsheetColumnValueType data_type, const eSpreadsheetFilterOperation operation)
static void set_filter_expand_flag(const bContext *, Panel *panel, short expand_flag)
static std::string value_string(const SpreadsheetRowFilter &row_filter, const eSpreadsheetColumnValueType data_type)
static void spreadsheet_row_filters_layout(const bContext *C, Panel *panel)
static void filter_panel_id_fn(void *, char *r_name)
static void filter_reorder(bContext *C, Panel *panel, int new_index)
static void spreadsheet_filter_panel_draw_header(const bContext *C, Panel *panel)
SpreadsheetTable * get_active_table(SpaceSpreadsheet &sspreadsheet)
static const SpreadsheetColumn * lookup_visible_column_for_filter(const SpaceSpreadsheet &sspreadsheet, const StringRef column_name)
static short get_filter_expand_flag(const bContext *, Panel *panel)
void register_row_filter_panels(ARegionType &region_type)
void RNA_int_set(PointerRNA *ptr, const char *name, int value)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
ListBase paneltypes
ListBase panels
void * first
void(* reorder)(bContext *C, Panel *pa, int new_index)
void(* set_list_data_expand_flag)(const bContext *C, Panel *pa, short expand_flag)
void(* draw)(const bContext *C, Panel *panel)
char idname[BKE_ST_MAXNAME]
char translation_context[BKE_ST_MAXNAME]
char category[BKE_ST_MAXNAME]
char label[BKE_ST_MAXNAME]
short(* get_list_data_expand_flag)(const bContext *C, Panel *pa)
void(* draw_header)(const bContext *C, Panel *panel)
struct PanelType * type
struct uiLayout * layout
struct Panel * next
void * data
Definition RNA_types.hh:53
SpreadsheetColumn ** columns
PointerRNA op(wmOperatorType *ot, std::optional< blender::StringRef > name, int icon, wmOperatorCallContext context, eUI_Item_Flag flag)
void label(blender::StringRef name, int icon)
void separator(float factor=1.0f, LayoutSeparatorType type=LayoutSeparatorType::Auto)
uiLayout & row(bool align)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
#define N_(msgid)