Blender V4.3
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 "spreadsheet_column.hh"
28#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 StringRef column_name)
122{
123 LISTBASE_FOREACH (SpreadsheetColumn *, column, &sspreadsheet.columns) {
124 if (column->display_name == column_name) {
125 return column;
126 }
127 }
128 return nullptr;
129}
130
132{
133 uiLayout *layout = panel->layout;
135 PointerRNA *filter_ptr = UI_panel_custom_data_get(panel);
136 const SpreadsheetRowFilter *filter = (SpreadsheetRowFilter *)filter_ptr->data;
137 const StringRef column_name = filter->column_name;
138 const eSpreadsheetFilterOperation operation = (eSpreadsheetFilterOperation)filter->operation;
139
140 const SpreadsheetColumn *column = lookup_visible_column_for_filter(*sspreadsheet, column_name);
141 if (!(sspreadsheet->filter_flag & SPREADSHEET_FILTER_ENABLE) ||
142 (column == nullptr && !column_name.is_empty()))
143 {
144 uiLayoutSetActive(layout, false);
145 }
146
147 uiLayout *row = uiLayoutRow(layout, true);
149 uiItemR(row, filter_ptr, "enabled", UI_ITEM_R_ICON_ONLY, "", ICON_NONE);
150
151 if (column_name.is_empty()) {
152 uiItemL(row, IFACE_("Filter"), ICON_NONE);
153 }
154 else if (column == nullptr) {
155 uiItemL(row, column_name.data(), ICON_NONE);
156 }
157 else {
159 std::stringstream ss;
160 ss << column_name;
161 ss << " ";
162 ss << operation_string(data_type, operation);
163 ss << " ";
164 ss << value_string(*filter, data_type);
165 uiItemL(row, ss.str().c_str(), ICON_NONE);
166 }
167
168 row = uiLayoutRow(layout, true);
170 const int current_index = BLI_findindex(&sspreadsheet->row_filters, filter);
171 uiItemIntO(row, "", ICON_X, "SPREADSHEET_OT_remove_row_filter_rule", "index", current_index);
172
173 /* Some padding so the X isn't too close to the drag icon. */
174 uiItemS_ex(layout, 0.25f);
175}
176
177static void spreadsheet_filter_panel_draw(const bContext *C, Panel *panel)
178{
179 uiLayout *layout = panel->layout;
181 PointerRNA *filter_ptr = UI_panel_custom_data_get(panel);
182 SpreadsheetRowFilter *filter = (SpreadsheetRowFilter *)filter_ptr->data;
183 const StringRef column_name = filter->column_name;
184 const eSpreadsheetFilterOperation operation = (eSpreadsheetFilterOperation)filter->operation;
185
186 const SpreadsheetColumn *column = lookup_visible_column_for_filter(*sspreadsheet, column_name);
187 if (!(sspreadsheet->filter_flag & SPREADSHEET_FILTER_ENABLE) ||
188 !(filter->flag & SPREADSHEET_ROW_FILTER_ENABLED) ||
189 (column == nullptr && !column_name.is_empty()))
190 {
191 uiLayoutSetActive(layout, false);
192 }
193
194 uiLayoutSetPropSep(layout, true);
195 uiLayoutSetPropDecorate(layout, false);
196
197 uiItemR(layout, filter_ptr, "column_name", UI_ITEM_NONE, IFACE_("Column"), ICON_NONE);
198
199 /* Don't draw settings for filters with no corresponding visible column. */
200 if (column == nullptr || column_name.is_empty()) {
201 return;
202 }
203
204 switch (static_cast<eSpreadsheetColumnValueType>(column->data_type)) {
206 uiItemR(layout, filter_ptr, "operation", UI_ITEM_NONE, nullptr, ICON_NONE);
207 uiItemR(layout, filter_ptr, "value_int8", UI_ITEM_NONE, IFACE_("Value"), ICON_NONE);
208 break;
210 uiItemR(layout, filter_ptr, "operation", UI_ITEM_NONE, nullptr, ICON_NONE);
211 uiItemR(layout, filter_ptr, "value_int", UI_ITEM_NONE, IFACE_("Value"), ICON_NONE);
212 break;
214 uiItemR(layout, filter_ptr, "operation", UI_ITEM_NONE, nullptr, ICON_NONE);
215 uiItemR(layout, filter_ptr, "value_int2", UI_ITEM_NONE, IFACE_("Value"), ICON_NONE);
216 break;
218 uiItemR(layout, filter_ptr, "operation", UI_ITEM_NONE, nullptr, ICON_NONE);
219 uiItemR(layout, filter_ptr, "value_float", UI_ITEM_NONE, IFACE_("Value"), ICON_NONE);
220 if (operation == SPREADSHEET_ROW_FILTER_EQUAL) {
221 uiItemR(layout, filter_ptr, "threshold", UI_ITEM_NONE, nullptr, ICON_NONE);
222 }
223 break;
225 uiItemR(layout, filter_ptr, "operation", UI_ITEM_NONE, nullptr, ICON_NONE);
226 uiItemR(layout, filter_ptr, "value_float2", UI_ITEM_NONE, IFACE_("Value"), ICON_NONE);
227 if (operation == SPREADSHEET_ROW_FILTER_EQUAL) {
228 uiItemR(layout, filter_ptr, "threshold", UI_ITEM_NONE, nullptr, ICON_NONE);
229 }
230 break;
232 uiItemR(layout, filter_ptr, "operation", UI_ITEM_NONE, nullptr, ICON_NONE);
233 uiItemR(layout, filter_ptr, "value_float3", UI_ITEM_NONE, IFACE_("Value"), ICON_NONE);
234 if (operation == SPREADSHEET_ROW_FILTER_EQUAL) {
235 uiItemR(layout, filter_ptr, "threshold", UI_ITEM_NONE, nullptr, ICON_NONE);
236 }
237 break;
239 uiItemR(layout, filter_ptr, "value_boolean", UI_ITEM_NONE, IFACE_("Value"), ICON_NONE);
240 break;
242 uiItemR(layout, filter_ptr, "value_string", UI_ITEM_NONE, IFACE_("Value"), ICON_NONE);
243 break;
246 uiItemR(layout, filter_ptr, "operation", UI_ITEM_NONE, nullptr, ICON_NONE);
247 uiItemR(layout, filter_ptr, "value_color", UI_ITEM_NONE, IFACE_("Value"), ICON_NONE);
248 if (operation == SPREADSHEET_ROW_FILTER_EQUAL) {
249 uiItemR(layout, filter_ptr, "threshold", UI_ITEM_NONE, nullptr, ICON_NONE);
250 }
251 break;
253 uiItemR(layout, filter_ptr, "value_string", UI_ITEM_NONE, IFACE_("Value"), ICON_NONE);
254 break;
258 uiItemL(layout, IFACE_("Unsupported column type"), ICON_ERROR);
259 break;
260 }
261}
262
263static void spreadsheet_row_filters_layout(const bContext *C, Panel *panel)
264{
265 uiLayout *layout = panel->layout;
266 ARegion *region = CTX_wm_region(C);
267 bScreen *screen = CTX_wm_screen(C);
269 ListBase *row_filters = &sspreadsheet->row_filters;
270
271 if (!(sspreadsheet->filter_flag & SPREADSHEET_FILTER_ENABLE)) {
272 uiLayoutSetActive(layout, false);
273 }
274
275 uiItemO(layout, nullptr, ICON_ADD, "SPREADSHEET_OT_add_row_filter_rule");
276
277 const bool panels_match = UI_panel_list_matches_data(region, row_filters, filter_panel_id_fn);
278
279 if (!panels_match) {
280 UI_panels_free_instanced(C, region);
281 LISTBASE_FOREACH (SpreadsheetRowFilter *, row_filter, row_filters) {
282 char panel_idname[MAX_NAME];
283 filter_panel_id_fn(row_filter, panel_idname);
284
285 PointerRNA *filter_ptr = MEM_new<PointerRNA>("panel customdata");
286 *filter_ptr = RNA_pointer_create(&screen->id, &RNA_SpreadsheetRowFilter, row_filter);
287
288 UI_panel_add_instanced(C, region, &region->panels, panel_idname, filter_ptr);
289 }
290 }
291 else {
292 /* Assuming there's only one group of instanced panels, update the custom data pointers. */
293 Panel *panel_iter = (Panel *)region->panels.first;
294 LISTBASE_FOREACH (SpreadsheetRowFilter *, row_filter, row_filters) {
295
296 /* Move to the next instanced panel corresponding to the next filter. */
297 while ((panel_iter->type == nullptr) || !(panel_iter->type->flag & PANEL_TYPE_INSTANCED)) {
298 panel_iter = panel_iter->next;
299 BLI_assert(panel_iter != nullptr); /* There shouldn't be fewer panels than filters. */
300 }
301
302 PointerRNA *filter_ptr = MEM_new<PointerRNA>("panel customdata");
303 *filter_ptr = RNA_pointer_create(&screen->id, &RNA_SpreadsheetRowFilter, row_filter);
304 UI_panel_custom_data_set(panel_iter, filter_ptr);
305
306 panel_iter = panel_iter->next;
307 }
308 }
309}
310
311static void filter_reorder(bContext *C, Panel *panel, int new_index)
312{
314 ListBase *row_filters = &sspreadsheet->row_filters;
315 PointerRNA *filter_ptr = UI_panel_custom_data_get(panel);
316 SpreadsheetRowFilter *filter = (SpreadsheetRowFilter *)filter_ptr->data;
317
318 int current_index = BLI_findindex(row_filters, filter);
319 BLI_assert(current_index >= 0);
320 BLI_assert(new_index >= 0);
321
322 BLI_listbase_link_move(row_filters, filter, new_index - current_index);
323}
324
325static short get_filter_expand_flag(const bContext * /*C*/, Panel *panel)
326{
327 PointerRNA *filter_ptr = UI_panel_custom_data_get(panel);
328 SpreadsheetRowFilter *filter = (SpreadsheetRowFilter *)filter_ptr->data;
329
330 return short(filter->flag) & SPREADSHEET_ROW_FILTER_UI_EXPAND;
331}
332
333static void set_filter_expand_flag(const bContext * /*C*/, Panel *panel, short expand_flag)
334{
335 PointerRNA *filter_ptr = UI_panel_custom_data_get(panel);
336 SpreadsheetRowFilter *filter = (SpreadsheetRowFilter *)filter_ptr->data;
337
338 SET_FLAG_FROM_TEST(filter->flag,
341}
342
344{
345 {
346 PanelType *panel_type = MEM_cnew<PanelType>(__func__);
347 STRNCPY(panel_type->idname, "SPREADSHEET_PT_row_filters");
348 STRNCPY(panel_type->label, N_("Filters"));
349 STRNCPY(panel_type->category, "Filters");
351 panel_type->flag = PANEL_TYPE_NO_HEADER;
353 BLI_addtail(&region_type.paneltypes, panel_type);
354 }
355
356 {
357 PanelType *panel_type = MEM_cnew<PanelType>(__func__);
358 STRNCPY(panel_type->idname, "SPREADSHEET_PT_filter");
359 STRNCPY(panel_type->label, "");
360 STRNCPY(panel_type->category, "Filters");
367 panel_type->reorder = filter_reorder;
368 BLI_addtail(&region_type.paneltypes, panel_type);
369 }
370}
371
372} // 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:66
@ PANEL_TYPE_NO_HEADER
@ PANEL_TYPE_INSTANCED
@ PANEL_TYPE_HEADER_EXPAND
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
#define LISTBASE_FOREACH(type, var, list)
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
void void void bool BLI_listbase_link_move(ListBase *listbase, void *vlink, int step) ATTR_NONNULL()
Definition listbase.cc:435
int BLI_findindex(const struct ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
#define STRNCPY(dst, src)
Definition BLI_string.h:593
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
#define MAX_NAME
Definition DNA_defs.h:50
@ 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
void uiLayoutSetActive(uiLayout *layout, bool active)
@ UI_EMBOSS_NONE
void uiItemIntO(uiLayout *layout, const char *name, int icon, const char *opname, const char *propname, int value)
void uiItemL(uiLayout *layout, const char *name, int icon)
void UI_panels_free_instanced(const bContext *C, ARegion *region)
uiLayout * uiLayoutRow(uiLayout *layout, bool align)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
void uiItemS_ex(uiLayout *layout, float factor, LayoutSeparatorType type=LayoutSeparatorType::Auto)
void UI_panel_custom_data_set(Panel *panel, PointerRNA *custom_data)
#define UI_ITEM_NONE
void uiLayoutSetEmboss(uiLayout *layout, eUIEmbossType emboss)
void uiItemO(uiLayout *layout, const char *name, int icon, const char *opname)
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
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)
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
@ UI_ITEM_R_ICON_ONLY
constexpr bool is_empty() const
constexpr const char * data() const
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 SpreadsheetColumn * lookup_visible_column_for_filter(const SpaceSpreadsheet &sspreadsheet, const StringRef column_name)
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)
static short get_filter_expand_flag(const bContext *, Panel *panel)
void register_row_filter_panels(ARegionType &region_type)
PointerRNA RNA_pointer_create(ID *id, StructRNA *type, void *data)
ListBase paneltypes
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:42
#define N_(msgid)