Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Goudey <h.goudey@me.com>2021-06-19 00:33:02 +0300
committerHans Goudey <h.goudey@me.com>2021-06-19 00:33:02 +0300
commitf9aea19d98908be450f228a35bb6098e7e3e4b03 (patch)
tree87e91bc56f4235a86bcd44706b0f76064131e3df /source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc
parentd52b7dbe2695c673b3bad091b55893413e7b022b (diff)
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
Diffstat (limited to 'source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc')
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc347
1 files changed, 347 insertions, 0 deletions
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc
new file mode 100644
index 00000000000..dbd2ef157af
--- /dev/null
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc
@@ -0,0 +1,347 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <cstring>
+
+#include "BLI_listbase.h"
+#include "BLI_string.h"
+#include "BLI_string_ref.hh"
+
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+
+#include "BKE_screen.h"
+
+#include "RNA_access.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "BLT_translation.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "spreadsheet_column.hh"
+#include "spreadsheet_intern.hh"
+#include "spreadsheet_row_filter.hh"
+#include "spreadsheet_row_filter_ui.hh"
+
+using namespace blender;
+using namespace blender::ed::spreadsheet;
+
+static void filter_panel_id_fn(void *UNUSED(row_filter_v), char *r_name)
+{
+ /* All row filters use the same panel ID. */
+ BLI_snprintf(r_name, BKE_ST_MAXNAME, "SPREADSHEET_PT_filter");
+}
+
+static std::string operation_string(const eSpreadsheetColumnValueType data_type,
+ const eSpreadsheetFilterOperation operation)
+{
+ if (ELEM(data_type,
+ SPREADSHEET_VALUE_TYPE_BOOL,
+ SPREADSHEET_VALUE_TYPE_INSTANCES,
+ SPREADSHEET_VALUE_TYPE_COLOR)) {
+ return "=";
+ }
+
+ switch (operation) {
+ case SPREADSHEET_ROW_FILTER_EQUAL:
+ return "=";
+ case SPREADSHEET_ROW_FILTER_GREATER:
+ return ">";
+ case SPREADSHEET_ROW_FILTER_LESS:
+ return "<";
+ }
+ BLI_assert_unreachable();
+ return "";
+}
+
+static std::string value_string(const SpreadsheetRowFilter &row_filter,
+ const eSpreadsheetColumnValueType data_type)
+{
+ switch (data_type) {
+ case SPREADSHEET_VALUE_TYPE_INT32:
+ return std::to_string(row_filter.value_int);
+ case SPREADSHEET_VALUE_TYPE_FLOAT: {
+ std::ostringstream result;
+ result.precision(3);
+ result << std::fixed << row_filter.value_float;
+ return result.str();
+ }
+ case SPREADSHEET_VALUE_TYPE_FLOAT2: {
+ std::ostringstream result;
+ result.precision(3);
+ result << std::fixed << "(" << row_filter.value_float2[0] << ", "
+ << row_filter.value_float2[1] << ")";
+ return result.str();
+ }
+ case SPREADSHEET_VALUE_TYPE_FLOAT3: {
+ std::ostringstream result;
+ result.precision(3);
+ result << std::fixed << "(" << row_filter.value_float3[0] << ", "
+ << row_filter.value_float3[1] << ", " << row_filter.value_float3[2] << ")";
+ return result.str();
+ }
+ case SPREADSHEET_VALUE_TYPE_BOOL:
+ return (row_filter.flag & SPREADSHEET_ROW_FILTER_BOOL_VALUE) ? IFACE_("True") :
+ IFACE_("False");
+ case SPREADSHEET_VALUE_TYPE_INSTANCES:
+ if (row_filter.value_string != nullptr) {
+ return row_filter.value_string;
+ }
+ return "";
+ case SPREADSHEET_VALUE_TYPE_COLOR:
+ std::ostringstream result;
+ result.precision(3);
+ result << std::fixed << "(" << row_filter.value_color[0] << ", " << row_filter.value_color[1]
+ << ", " << row_filter.value_color[2] << ", " << row_filter.value_color[3] << ")";
+ return result.str();
+ }
+ BLI_assert_unreachable();
+ return "";
+}
+
+static SpreadsheetColumn *lookup_visible_column_for_filter(const SpaceSpreadsheet &sspreadsheet,
+ const StringRef column_name)
+{
+ LISTBASE_FOREACH (SpreadsheetColumn *, column, &sspreadsheet.columns) {
+ if (column->display_name == column_name) {
+ return column;
+ }
+ }
+ return nullptr;
+}
+
+static void spreadsheet_filter_panel_draw_header(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+ SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
+ PointerRNA *filter_ptr = UI_panel_custom_data_get(panel);
+ const SpreadsheetRowFilter *filter = (SpreadsheetRowFilter *)filter_ptr->data;
+ const StringRef column_name = filter->column_name;
+ const eSpreadsheetFilterOperation operation = (eSpreadsheetFilterOperation)filter->operation;
+
+ const SpreadsheetColumn *column = lookup_visible_column_for_filter(*sspreadsheet, column_name);
+ if (!(sspreadsheet->filter_flag & SPREADSHEET_FILTER_ENABLE) ||
+ (column == nullptr && !column_name.is_empty())) {
+ uiLayoutSetActive(layout, false);
+ }
+
+ uiLayout *row = uiLayoutRow(layout, true);
+ uiLayoutSetEmboss(row, UI_EMBOSS_NONE);
+ uiItemR(row, filter_ptr, "enabled", UI_ITEM_R_ICON_ONLY, "", ICON_NONE);
+
+ if (column_name.is_empty()) {
+ uiItemL(row, IFACE_("Filter"), ICON_NONE);
+ }
+ else if (column == nullptr) {
+ uiItemL(row, column_name.data(), ICON_NONE);
+ }
+ else {
+ const eSpreadsheetColumnValueType data_type = (eSpreadsheetColumnValueType)column->data_type;
+ std::stringstream ss;
+ ss << column_name;
+ ss << " ";
+ ss << operation_string(data_type, operation);
+ ss << " ";
+ ss << value_string(*filter, data_type);
+ uiItemL(row, ss.str().c_str(), ICON_NONE);
+ }
+
+ row = uiLayoutRow(layout, true);
+ uiLayoutSetEmboss(row, UI_EMBOSS_NONE);
+ const int current_index = BLI_findindex(&sspreadsheet->row_filters, filter);
+ uiItemIntO(row, "", ICON_X, "SPREADSHEET_OT_remove_row_filter_rule", "index", current_index);
+
+ /* Some padding so the X isn't too close to the drag icon. */
+ uiItemS_ex(layout, 0.25f);
+}
+
+static void spreadsheet_filter_panel_draw(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+ SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
+ PointerRNA *filter_ptr = UI_panel_custom_data_get(panel);
+ SpreadsheetRowFilter *filter = (SpreadsheetRowFilter *)filter_ptr->data;
+ const StringRef column_name = filter->column_name;
+ const eSpreadsheetFilterOperation operation = (eSpreadsheetFilterOperation)filter->operation;
+
+ const SpreadsheetColumn *column = lookup_visible_column_for_filter(*sspreadsheet, column_name);
+ if (!(sspreadsheet->filter_flag & SPREADSHEET_FILTER_ENABLE) ||
+ !(filter->flag & SPREADSHEET_ROW_FILTER_ENABLED) ||
+ (column == nullptr && !column_name.is_empty())) {
+ uiLayoutSetActive(layout, false);
+ }
+
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+
+ uiItemR(layout, filter_ptr, "column_name", 0, IFACE_("Column"), ICON_NONE);
+
+ /* Don't draw settings for filters with no corresponding visible column. */
+ if (column == nullptr || column_name.is_empty()) {
+ return;
+ }
+
+ switch (static_cast<eSpreadsheetColumnValueType>(column->data_type)) {
+ case SPREADSHEET_VALUE_TYPE_INT32:
+ uiItemR(layout, filter_ptr, "operation", 0, nullptr, ICON_NONE);
+ uiItemR(layout, filter_ptr, "value_int", 0, IFACE_("Value"), ICON_NONE);
+ break;
+ case SPREADSHEET_VALUE_TYPE_FLOAT:
+ uiItemR(layout, filter_ptr, "operation", 0, nullptr, ICON_NONE);
+ uiItemR(layout, filter_ptr, "value_float", 0, IFACE_("Value"), ICON_NONE);
+ if (operation == SPREADSHEET_ROW_FILTER_EQUAL) {
+ uiItemR(layout, filter_ptr, "threshold", 0, nullptr, ICON_NONE);
+ }
+ break;
+ case SPREADSHEET_VALUE_TYPE_FLOAT2:
+ uiItemR(layout, filter_ptr, "operation", 0, nullptr, ICON_NONE);
+ uiItemR(layout, filter_ptr, "value_float2", 0, IFACE_("Value"), ICON_NONE);
+ if (operation == SPREADSHEET_ROW_FILTER_EQUAL) {
+ uiItemR(layout, filter_ptr, "threshold", 0, nullptr, ICON_NONE);
+ }
+ break;
+ case SPREADSHEET_VALUE_TYPE_FLOAT3:
+ uiItemR(layout, filter_ptr, "operation", 0, nullptr, ICON_NONE);
+ uiItemR(layout, filter_ptr, "value_float3", 0, IFACE_("Value"), ICON_NONE);
+ if (operation == SPREADSHEET_ROW_FILTER_EQUAL) {
+ uiItemR(layout, filter_ptr, "threshold", 0, nullptr, ICON_NONE);
+ }
+ break;
+ case SPREADSHEET_VALUE_TYPE_BOOL:
+ uiItemR(layout, filter_ptr, "value_boolean", 0, IFACE_("Value"), ICON_NONE);
+ break;
+ case SPREADSHEET_VALUE_TYPE_INSTANCES:
+ uiItemR(layout, filter_ptr, "value_string", 0, IFACE_("Value"), ICON_NONE);
+ break;
+ case SPREADSHEET_VALUE_TYPE_COLOR:
+ uiItemR(layout, filter_ptr, "value_color", 0, IFACE_("Value"), ICON_NONE);
+ uiItemR(layout, filter_ptr, "threshold", 0, nullptr, ICON_NONE);
+ break;
+ }
+}
+
+static void spreadsheet_row_filters_layout(const bContext *C, Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+ ARegion *region = CTX_wm_region(C);
+ bScreen *screen = CTX_wm_screen(C);
+ SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
+ ListBase *row_filters = &sspreadsheet->row_filters;
+
+ if (!(sspreadsheet->filter_flag & SPREADSHEET_FILTER_ENABLE)) {
+ uiLayoutSetActive(layout, false);
+ }
+
+ uiItemO(layout, nullptr, ICON_ADD, "SPREADSHEET_OT_add_row_filter_rule");
+
+ const bool panels_match = UI_panel_list_matches_data(region, row_filters, filter_panel_id_fn);
+
+ if (!panels_match) {
+ UI_panels_free_instanced(C, region);
+ LISTBASE_FOREACH (SpreadsheetRowFilter *, row_filter, row_filters) {
+ char panel_idname[MAX_NAME];
+ filter_panel_id_fn(row_filter, panel_idname);
+
+ PointerRNA *filter_ptr = (PointerRNA *)MEM_mallocN(sizeof(PointerRNA), "panel customdata");
+ RNA_pointer_create(&screen->id, &RNA_SpreadsheetRowFilter, row_filter, filter_ptr);
+
+ UI_panel_add_instanced(C, region, &region->panels, panel_idname, filter_ptr);
+ }
+ }
+ else {
+ /* Assuming there's only one group of instanced panels, update the custom data pointers. */
+ Panel *panel = (Panel *)region->panels.first;
+ LISTBASE_FOREACH (SpreadsheetRowFilter *, row_filter, row_filters) {
+
+ /* Move to the next instanced panel corresponding to the next filter. */
+ while ((panel->type == nullptr) || !(panel->type->flag & PANEL_TYPE_INSTANCED)) {
+ panel = panel->next;
+ BLI_assert(panel != nullptr); /* There shouldn't be fewer panels than filters. */
+ }
+
+ PointerRNA *filter_ptr = (PointerRNA *)MEM_mallocN(sizeof(PointerRNA), "panel customdata");
+ RNA_pointer_create(&screen->id, &RNA_SpreadsheetRowFilter, row_filter, filter_ptr);
+ UI_panel_custom_data_set(panel, filter_ptr);
+
+ panel = panel->next;
+ }
+ }
+}
+
+static void filter_reorder(bContext *C, Panel *panel, int new_index)
+{
+ SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
+ ListBase *row_filters = &sspreadsheet->row_filters;
+ PointerRNA *filter_ptr = UI_panel_custom_data_get(panel);
+ SpreadsheetRowFilter *filter = (SpreadsheetRowFilter *)filter_ptr->data;
+
+ int current_index = BLI_findindex(row_filters, filter);
+ BLI_assert(current_index >= 0);
+ BLI_assert(new_index >= 0);
+
+ BLI_listbase_link_move(row_filters, filter, new_index - current_index);
+}
+
+static short get_filter_expand_flag(const bContext *UNUSED(C), Panel *panel)
+{
+ PointerRNA *filter_ptr = UI_panel_custom_data_get(panel);
+ SpreadsheetRowFilter *filter = (SpreadsheetRowFilter *)filter_ptr->data;
+
+ return (short)filter->flag & SPREADSHEET_ROW_FILTER_UI_EXPAND;
+}
+
+static void set_filter_expand_flag(const bContext *UNUSED(C), Panel *panel, short expand_flag)
+{
+ PointerRNA *filter_ptr = UI_panel_custom_data_get(panel);
+ SpreadsheetRowFilter *filter = (SpreadsheetRowFilter *)filter_ptr->data;
+
+ SET_FLAG_FROM_TEST(filter->flag,
+ expand_flag & SPREADSHEET_ROW_FILTER_UI_EXPAND,
+ SPREADSHEET_ROW_FILTER_UI_EXPAND);
+}
+
+void register_row_filter_panels(ARegionType &region_type)
+{
+ {
+ PanelType *panel_type = (PanelType *)MEM_callocN(sizeof(PanelType), __func__);
+ strcpy(panel_type->idname, "SPREADSHEET_PT_row_filters");
+ strcpy(panel_type->label, N_("Filters"));
+ strcpy(panel_type->category, "Filters");
+ strcpy(panel_type->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
+ panel_type->flag = PANEL_TYPE_NO_HEADER;
+ panel_type->draw = spreadsheet_row_filters_layout;
+ BLI_addtail(&region_type.paneltypes, panel_type);
+ }
+
+ {
+ PanelType *panel_type = (PanelType *)MEM_callocN(sizeof(PanelType), __func__);
+ strcpy(panel_type->idname, "SPREADSHEET_PT_filter");
+ strcpy(panel_type->label, "");
+ strcpy(panel_type->category, "Filters");
+ strcpy(panel_type->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
+ panel_type->flag = PANEL_TYPE_INSTANCED | PANEL_TYPE_DRAW_BOX | PANEL_TYPE_HEADER_EXPAND;
+ panel_type->draw_header = spreadsheet_filter_panel_draw_header;
+ panel_type->draw = spreadsheet_filter_panel_draw;
+ panel_type->get_list_data_expand_flag = get_filter_expand_flag;
+ panel_type->set_list_data_expand_flag = set_filter_expand_flag;
+ panel_type->reorder = filter_reorder;
+ BLI_addtail(&region_type.paneltypes, panel_type);
+ }
+}