From ae085e301c2aac0d6956609bfe93a90a19f0e235 Mon Sep 17 00:00:00 2001 From: Fabian Schempp Date: Fri, 25 Jun 2021 07:57:24 +0200 Subject: Spreadsheet: Dataset region for spreadsheet editor This patch adds a left aligned sidebar to the spreadsheet editor. This Sidebar can be used to navigate the geometry component types and attribute domains. It also provides a quick overview of domain sizes. It replaces the two dropdowns in the regions header. Next step will be to add the domain cycling shortcut using the CTRL + mouse wheel. Reviewer: Dalai Felinto (dfelinto), Julian Eisel (Severin), Hans Goudey (HooglyBoogly). Differential Revision: https://developer.blender.org/D11046 --- .../editors/space_spreadsheet/CMakeLists.txt | 4 + .../editors/space_spreadsheet/space_spreadsheet.cc | 109 +++++++- .../space_spreadsheet/spreadsheet_context.cc | 4 +- .../spreadsheet_data_source_geometry.cc | 9 +- .../space_spreadsheet/spreadsheet_dataset_draw.cc | 277 +++++++++++++++++++++ .../space_spreadsheet/spreadsheet_dataset_draw.hh | 64 +++++ .../spreadsheet_dataset_layout.cc | 112 +++++++++ .../spreadsheet_dataset_layout.hh | 68 +++++ .../space_spreadsheet/spreadsheet_intern.hh | 13 + .../editors/space_spreadsheet/spreadsheet_ops.cc | 53 ++++ 10 files changed, 695 insertions(+), 18 deletions(-) create mode 100644 source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc create mode 100644 source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.hh create mode 100644 source/blender/editors/space_spreadsheet/spreadsheet_dataset_layout.cc create mode 100644 source/blender/editors/space_spreadsheet/spreadsheet_dataset_layout.hh (limited to 'source/blender/editors/space_spreadsheet') diff --git a/source/blender/editors/space_spreadsheet/CMakeLists.txt b/source/blender/editors/space_spreadsheet/CMakeLists.txt index 7e3f3db9bc8..1ea6593588a 100644 --- a/source/blender/editors/space_spreadsheet/CMakeLists.txt +++ b/source/blender/editors/space_spreadsheet/CMakeLists.txt @@ -38,6 +38,8 @@ set(SRC spreadsheet_column.cc spreadsheet_data_source.cc spreadsheet_data_source_geometry.cc + spreadsheet_dataset_draw.cc + spreadsheet_dataset_layout.cc spreadsheet_draw.cc spreadsheet_layout.cc spreadsheet_ops.cc @@ -50,6 +52,8 @@ set(SRC spreadsheet_column_values.hh spreadsheet_data_source.hh spreadsheet_data_source_geometry.hh + spreadsheet_dataset_draw.hh + spreadsheet_dataset_layout.hh spreadsheet_draw.hh spreadsheet_intern.hh spreadsheet_layout.hh diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc index 8c42f28b5f4..f8654500044 100644 --- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc +++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc @@ -47,6 +47,7 @@ #include "spreadsheet_context.hh" #include "spreadsheet_data_source_geometry.hh" +#include "spreadsheet_dataset_draw.hh" #include "spreadsheet_intern.hh" #include "spreadsheet_layout.hh" #include "spreadsheet_row_filter.hh" @@ -79,6 +80,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; } + { + /* Dataset Region */ + ARegion *region = (ARegion *)MEM_callocN(sizeof(ARegion), "spreadsheet dataset region"); + BLI_addtail(&spreadsheet_space->regionbase, region); + region->regiontype = RGN_TYPE_CHANNELS; + region->alignment = RGN_ALIGN_LEFT; + region->v2d.scroll = (V2D_SCROLL_RIGHT | V2D_SCROLL_BOTTOM); + } + { /* Properties region. */ ARegion *region = (ARegion *)MEM_callocN(sizeof(ARegion), "spreadsheet right region"); @@ -195,7 +205,7 @@ static void spreadsheet_main_region_init(wmWindowManager *wm, ARegion *region) } } -ID *ED_spreadsheet_get_current_id(struct SpaceSpreadsheet *sspreadsheet) +ID *ED_spreadsheet_get_current_id(const struct SpaceSpreadsheet *sspreadsheet) { if (BLI_listbase_is_empty(&sspreadsheet->context_path)) { return nullptr; @@ -263,7 +273,7 @@ static void update_context_path_from_context(const bContext *C) } } -static void update_context_path(const bContext *C) +void spreadsheet_update_context_path(const bContext *C) { SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); if (sspreadsheet->flag & SPREADSHEET_FLAG_PINNED) { @@ -274,28 +284,40 @@ static void update_context_path(const bContext *C) } } -static std::unique_ptr get_data_source(const bContext *C) +Object *spreadsheet_get_object_eval(const SpaceSpreadsheet *sspreadsheet, + const Depsgraph *depsgraph) { - Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); - SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); ID *used_id = ED_spreadsheet_get_current_id(sspreadsheet); if (used_id == nullptr) { - return {}; + return nullptr; } const ID_Type id_type = GS(used_id->name); if (id_type != ID_OB) { - return {}; + return nullptr; } Object *object_orig = (Object *)used_id; if (!ELEM(object_orig->type, OB_MESH, OB_POINTCLOUD, OB_VOLUME)) { - return {}; + return nullptr; } + Object *object_eval = DEG_get_evaluated_object(depsgraph, object_orig); if (object_eval == nullptr) { - return {}; + return nullptr; } - return data_source_from_geometry(C, object_eval); + return object_eval; +} + +static std::unique_ptr get_data_source(const bContext *C) +{ + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); + + Object *object_eval = spreadsheet_get_object_eval(sspreadsheet, depsgraph); + if (object_eval) { + return data_source_from_geometry(C, object_eval); + } + return {}; } static float get_column_width(const ColumnValues &values) @@ -358,7 +380,7 @@ static void update_visible_columns(ListBase &columns, DataSource &data_source) static void spreadsheet_main_region_draw(const bContext *C, ARegion *region) { SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); - update_context_path(C); + spreadsheet_update_context_path(C); std::unique_ptr data_source = get_data_source(C); if (!data_source) { @@ -442,7 +464,7 @@ static void spreadsheet_header_region_init(wmWindowManager *UNUSED(wm), ARegion static void spreadsheet_header_region_draw(const bContext *C, ARegion *region) { - update_context_path(C); + spreadsheet_update_context_path(C); ED_region_header(C, region); } @@ -534,6 +556,59 @@ static void spreadsheet_footer_region_listener(const wmRegionListenerParams *UNU { } +static void spreadsheet_dataset_region_listener(const wmRegionListenerParams *params) +{ + ARegion *region = params->region; + wmNotifier *wmn = params->notifier; + + switch (wmn->category) { + case NC_SCENE: { + switch (wmn->data) { + case ND_FRAME: + ED_region_tag_redraw(region); + break; + } + break; + } + case NC_TEXTURE: + ED_region_tag_redraw(region); + break; + } + + spreadsheet_header_region_listener(params); +} + +static void spreadsheet_dataset_region_init(wmWindowManager *wm, ARegion *region) +{ + region->v2d.scroll |= V2D_SCROLL_RIGHT; + region->v2d.scroll &= ~(V2D_SCROLL_LEFT | V2D_SCROLL_TOP | V2D_SCROLL_BOTTOM); + region->v2d.scroll |= V2D_SCROLL_HORIZONTAL_HIDE; + region->v2d.scroll |= V2D_SCROLL_VERTICAL_HIDE; + + UI_view2d_region_reinit(®ion->v2d, V2D_COMMONVIEW_LIST, region->winx, region->winy); + + wmKeyMap *keymap = WM_keymap_ensure( + wm->defaultconf, "Spreadsheet Generic", SPACE_SPREADSHEET, 0); + WM_event_add_keymap_handler(®ion->handlers, keymap); +} + +static void spreadsheet_dataset_region_draw(const bContext *C, ARegion *region) +{ + spreadsheet_update_context_path(C); + + View2D *v2d = ®ion->v2d; + UI_view2d_view_ortho(v2d); + UI_ThemeClearColor(TH_BACK); + + draw_dataset_in_region(C, region); + + /* reset view matrix */ + UI_view2d_view_restore(C); + + /* scrollers */ + UI_view2d_scrollers_draw(v2d, NULL); +} + static void spreadsheet_sidebar_init(wmWindowManager *wm, ARegion *region) { UI_panel_category_active_set_default(region, "Filters"); @@ -619,5 +694,15 @@ void ED_spacetype_spreadsheet(void) register_row_filter_panels(*art); + /* regions: channels */ + art = (ARegionType *)MEM_callocN(sizeof(ARegionType), "spreadsheet dataset region"); + art->regionid = RGN_TYPE_CHANNELS; + art->prefsizex = 200 + V2D_SCROLL_WIDTH; + art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D; + art->init = spreadsheet_dataset_region_init; + art->draw = spreadsheet_dataset_region_draw; + art->listener = spreadsheet_dataset_region_listener; + BLI_addhead(&st->regiontypes, art); + BKE_spacetype_register(st); } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_context.cc b/source/blender/editors/space_spreadsheet/spreadsheet_context.cc index 3eb43338908..af6ab5d1b92 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_context.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_context.cc @@ -255,11 +255,11 @@ void ED_spreadsheet_context_path_update_tag(SpaceSpreadsheet *sspreadsheet) blender::ed::spreadsheet::spreadsheet_context_update_tag(sspreadsheet); } -uint64_t ED_spreadsheet_context_path_hash(SpaceSpreadsheet *sspreadsheet) +uint64_t ED_spreadsheet_context_path_hash(const SpaceSpreadsheet *sspreadsheet) { BLI_HashMurmur2A mm2; BLI_hash_mm2a_init(&mm2, 1234); - LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) { + LISTBASE_FOREACH (const SpreadsheetContext *, context, &sspreadsheet->context_path) { blender::ed::spreadsheet::spreadsheet_context_hash(context, &mm2); } return BLI_hash_mm2a_end(&mm2); diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc index 0c76c8b7a15..6d244a1bda6 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -385,9 +385,9 @@ int InstancesDataSource::tot_rows() const return component_->instances_amount(); } -static GeometrySet get_display_geometry_set(SpaceSpreadsheet *sspreadsheet, - Object *object_eval, - const GeometryComponentType used_component_type) +GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *sspreadsheet, + Object *object_eval, + const GeometryComponentType used_component_type) { GeometrySet geometry_set; if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_ORIGINAL) { @@ -470,7 +470,8 @@ std::unique_ptr data_source_from_geometry(const bContext *C, Object SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); const AttributeDomain domain = (AttributeDomain)sspreadsheet->attribute_domain; const GeometryComponentType component_type = get_display_component_type(C, object_eval); - GeometrySet geometry_set = get_display_geometry_set(sspreadsheet, object_eval, component_type); + GeometrySet geometry_set = spreadsheet_get_display_geometry_set( + sspreadsheet, object_eval, component_type); if (!geometry_set.has(component_type)) { return {}; diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc new file mode 100644 index 00000000000..1fa6e47fcdf --- /dev/null +++ b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc @@ -0,0 +1,277 @@ +/* + * 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 + +#include "DNA_space_types.h" +#include "DNA_windowmanager_types.h" + +#include "BKE_context.h" + +#include "BLF_api.h" + +#include "BLI_rect.h" + +#include "RNA_access.h" + +#include "UI_interface.h" +#include "UI_view2d.h" + +#include "WM_types.h" + +#include "spreadsheet_dataset_draw.hh" +#include "spreadsheet_draw.hh" +#include "spreadsheet_intern.hh" + +static int is_component_row_selected(struct uiBut *but, const void *arg) +{ + SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)arg; + + GeometryComponentType component = (GeometryComponentType)UI_but_datasetrow_component_get(but); + AttributeDomain domain = (AttributeDomain)UI_but_datasetrow_domain_get(but); + + const bool is_component_selected = (GeometryComponentType) + sspreadsheet->geometry_component_type == component; + const bool is_domain_selected = (AttributeDomain)sspreadsheet->attribute_domain == domain; + bool is_selected = is_component_selected && is_domain_selected; + + if (component == GEO_COMPONENT_TYPE_INSTANCES) { + is_selected = is_component_selected; + } + + return is_selected; +} + +namespace blender::ed::spreadsheet { + +/* -------------------------------------------------------------------- */ +/* Draw Context */ + +class DatasetDrawContext { + std::array mval_; + + public: + const SpaceSpreadsheet *sspreadsheet; + Object *object_eval; + /* Current geometry set, changes per component. */ + GeometrySet current_geometry_set; + + DatasetDrawContext(const bContext *C); + + GeometrySet geometry_set_from_component(GeometryComponentType component); + const std::array &cursor_mval() const; +}; + +DatasetDrawContext::DatasetDrawContext(const bContext *C) + : sspreadsheet(CTX_wm_space_spreadsheet(C)), + object_eval(spreadsheet_get_object_eval(sspreadsheet, CTX_data_depsgraph_pointer(C))) +{ + const wmWindow *win = CTX_wm_window(C); + const ARegion *region = CTX_wm_region(C); + mval_ = {win->eventstate->x - region->winrct.xmin, win->eventstate->y - region->winrct.ymin}; +} + +GeometrySet DatasetDrawContext::geometry_set_from_component(GeometryComponentType component) +{ + return spreadsheet_get_display_geometry_set(sspreadsheet, object_eval, component); +} + +const std::array &DatasetDrawContext::cursor_mval() const +{ + return mval_; +} + +/* -------------------------------------------------------------------- */ +/* Drawer */ + +DatasetRegionDrawer::DatasetRegionDrawer(const ARegion *region, + uiBlock &block, + DatasetDrawContext &draw_context) + : row_height(UI_UNIT_Y), + xmin(region->v2d.cur.xmin), + xmax(region->v2d.cur.xmax), + block(block), + v2d(region->v2d), + draw_context(draw_context) +{ +} + +void DatasetRegionDrawer::draw_hierarchy(const DatasetLayoutHierarchy &layout) +{ + for (const DatasetComponentLayoutInfo &component : layout.components) { + draw_context.current_geometry_set = draw_context.geometry_set_from_component(component.type); + + draw_component_row(component); + + /* Iterate attribute domains, skip unset ones (storage has to be in a enum-based, fixed size + * array so uses optionals to support skipping enum values that shouldn't be displayed for a + * component). */ + for (auto &optional_domain : component.attr_domains) { + if (!optional_domain) { + continue; + } + + const DatasetAttrDomainLayoutInfo &domain_info = *optional_domain; + draw_attribute_domain_row(component, domain_info); + } + } +} + +static int element_count_from_component_domain(const GeometrySet &geometry_set, + GeometryComponentType component, + AttributeDomain domain) +{ + if (geometry_set.has_mesh() && component == GEO_COMPONENT_TYPE_MESH) { + const MeshComponent *mesh_component = geometry_set.get_component_for_read(); + return mesh_component->attribute_domain_size(domain); + } + + if (geometry_set.has_pointcloud() && component == GEO_COMPONENT_TYPE_POINT_CLOUD) { + const PointCloudComponent *point_cloud_component = + geometry_set.get_component_for_read(); + return point_cloud_component->attribute_domain_size(domain); + } + + if (geometry_set.has_instances() && component == GEO_COMPONENT_TYPE_INSTANCES) { + const InstancesComponent *instances_component = + geometry_set.get_component_for_read(); + return instances_component->instances_amount(); + } + + if (geometry_set.has_volume() && component == GEO_COMPONENT_TYPE_VOLUME) { + const VolumeComponent *volume_component = + geometry_set.get_component_for_read(); + return volume_component->attribute_domain_size(domain); + } + + if (geometry_set.has_curve() && component == GEO_COMPONENT_TYPE_CURVE) { + const CurveComponent *curve_component = geometry_set.get_component_for_read(); + return curve_component->attribute_domain_size(domain); + } + + return 0; +} + +void DatasetRegionDrawer::draw_dataset_row(const int indentation, + const GeometryComponentType component, + const std::optional domain, + BIFIconID icon, + const char *label, + const bool is_active) +{ + + const float row_height = UI_UNIT_Y; + const float padding_x = UI_UNIT_X * 0.25f; + + const rctf rect = {float(xmin) + padding_x, + float(xmax) - V2D_SCROLL_HANDLE_WIDTH, + ymin_offset - row_height, + ymin_offset}; + + char element_count[7]; + BLI_str_format_attribute_domain_size( + element_count, + domain ? element_count_from_component_domain( + draw_context.current_geometry_set, component, *domain) : + 0); + + std::string label_and_element_count = label; + label_and_element_count += UI_SEP_CHAR; + label_and_element_count += element_count; + + uiBut *bt = uiDefIconTextButO(&block, + UI_BTYPE_DATASETROW, + "SPREADSHEET_OT_change_spreadsheet_data_source", + 0, + icon, + label, + rect.xmin, + rect.ymin, + BLI_rctf_size_x(&rect), + BLI_rctf_size_y(&rect), + NULL); + + UI_but_datasetrow_indentation_set(bt, indentation); + + if (is_active) { + UI_but_hint_drawstr_set(bt, element_count); + UI_but_datasetrow_component_set(bt, component); + if (domain) { + UI_but_datasetrow_domain_set(bt, *domain); + } + UI_but_func_pushed_state_set(bt, &is_component_row_selected, draw_context.sspreadsheet); + + PointerRNA *but_ptr = UI_but_operator_ptr_get((uiBut *)bt); + RNA_int_set(but_ptr, "component_type", component); + if (domain) { + RNA_int_set(but_ptr, "attribute_domain_type", *domain); + } + } + + ymin_offset -= row_height; +} + +void DatasetRegionDrawer::draw_component_row(const DatasetComponentLayoutInfo &component_info) +{ + if (component_info.type == GEO_COMPONENT_TYPE_INSTANCES) { + draw_dataset_row( + 0, component_info.type, std::nullopt, component_info.icon, component_info.label, true); + } + else { + draw_dataset_row( + 0, component_info.type, std::nullopt, component_info.icon, component_info.label, false); + } +} + +void DatasetRegionDrawer::draw_attribute_domain_row( + const DatasetComponentLayoutInfo &component_info, + const DatasetAttrDomainLayoutInfo &domain_info) +{ + draw_dataset_row( + 1, component_info.type, domain_info.type, domain_info.icon, domain_info.label, true); +} + +/* -------------------------------------------------------------------- */ +/* Drawer */ + +void draw_dataset_in_region(const bContext *C, ARegion *region) +{ + DatasetDrawContext draw_context{C}; + if (!draw_context.object_eval) { + /* No object means nothing to display. Keep the region empty. */ + return; + } + + uiBlock *block = UI_block_begin(C, region, __func__, UI_EMBOSS); + + DatasetRegionDrawer drawer{region, *block, draw_context}; + + /* Start with an offset to align buttons to spreadsheet rows. Use spreadsheet drawing info for + * that. */ + drawer.ymin_offset = -SpreadsheetDrawer().top_row_height + drawer.row_height; + + const DatasetLayoutHierarchy hierarchy = dataset_layout_hierarchy(); + drawer.draw_hierarchy(hierarchy); +#ifndef NDEBUG + dataset_layout_hierarchy_sanity_check(hierarchy); +#endif + + UI_block_end(C, block); + UI_view2d_totRect_set(®ion->v2d, region->winx, abs(drawer.ymin_offset)); + UI_block_draw(C, block); +} + +} // namespace blender::ed::spreadsheet diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.hh b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.hh new file mode 100644 index 00000000000..d9e6d882c2a --- /dev/null +++ b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.hh @@ -0,0 +1,64 @@ +/* + * 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 + +#include "BKE_geometry_set.hh" +#include "UI_interface.h" +#include "spreadsheet_dataset_layout.hh" + +struct ARegion; +struct uiBlock; +struct View2D; +struct bContext; + +namespace blender::ed::spreadsheet { + +class DatasetDrawContext; + +class DatasetRegionDrawer { + public: + const int row_height; + float ymin_offset = 0; + + int xmin; + int xmax; + uiBlock █ + const View2D &v2d; + DatasetDrawContext &draw_context; + + DatasetRegionDrawer(const ARegion *region, uiBlock &block, DatasetDrawContext &draw_context); + + void draw_hierarchy(const DatasetLayoutHierarchy &layout); + + void draw_attribute_domain_row(const DatasetComponentLayoutInfo &component, + const DatasetAttrDomainLayoutInfo &domain_info); + void draw_component_row(const DatasetComponentLayoutInfo &component_info); + + private: + void draw_dataset_row(const int indentation, + const GeometryComponentType component, + const std::optional domain, + const BIFIconID icon, + const char *label, + const bool is_active); +}; + +void draw_dataset_in_region(const bContext *C, ARegion *region); + +} // namespace blender::ed::spreadsheet diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_layout.cc new file mode 100644 index 00000000000..e9f2c51a6da --- /dev/null +++ b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_layout.cc @@ -0,0 +1,112 @@ +/* + * 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 + +#include "BLI_span.hh" + +#include "BLT_translation.h" + +#include "spreadsheet_dataset_layout.hh" + +namespace blender::ed::spreadsheet { + +#define ATTR_INFO(type, label, icon) \ + std::optional \ + { \ + std::in_place, type, label, icon \ + } +#define ATTR_INFO_NONE(type) \ + { \ + std::nullopt \ + } + +/** + * Definion for the component->attribute-domain hierarchy. + * Constructed at compile time. + * + * \warning: Order of attribute-domains matters! It __must__ match the #AttributeDomain definition + * and fill gaps with unset optionals (i.e. `std::nullopt`). Would be nice to use array + * designators for this (which C++ doesn't support). + */ +constexpr DatasetComponentLayoutInfo DATASET_layout_hierarchy[] = { + { + GEO_COMPONENT_TYPE_MESH, + N_("Mesh"), + ICON_MESH_DATA, + { + ATTR_INFO(ATTR_DOMAIN_POINT, N_("Vertex"), ICON_VERTEXSEL), + ATTR_INFO(ATTR_DOMAIN_EDGE, N_("Edge"), ICON_EDGESEL), + ATTR_INFO(ATTR_DOMAIN_FACE, N_("Face"), ICON_FACESEL), + ATTR_INFO(ATTR_DOMAIN_CORNER, N_("Face Corner"), ICON_NODE_CORNER), + }, + }, + { + GEO_COMPONENT_TYPE_CURVE, + N_("Curves"), + ICON_CURVE_DATA, + { + ATTR_INFO(ATTR_DOMAIN_POINT, N_("Control Point"), ICON_CURVE_BEZCIRCLE), + ATTR_INFO_NONE(ATTR_DOMAIN_EDGE), + ATTR_INFO_NONE(ATTR_DOMAIN_CORNER), + ATTR_INFO_NONE(ATTR_DOMAIN_FACE), + ATTR_INFO(ATTR_DOMAIN_CURVE, N_("Spline"), ICON_CURVE_PATH), + }, + }, + { + GEO_COMPONENT_TYPE_POINT_CLOUD, + N_("Point Cloud"), + ICON_POINTCLOUD_DATA, + { + ATTR_INFO(ATTR_DOMAIN_POINT, N_("Point"), ICON_PARTICLE_POINT), + }, + }, + { + GEO_COMPONENT_TYPE_INSTANCES, + N_("Instances"), + ICON_EMPTY_AXIS, + {}, + }, +}; + +#undef ATTR_INFO +#undef ATTR_INFO_LABEL + +DatasetLayoutHierarchy dataset_layout_hierarchy() +{ + return DatasetLayoutHierarchy{ + Span{DATASET_layout_hierarchy, ARRAY_SIZE(DATASET_layout_hierarchy)}}; +} + +#ifndef NDEBUG +/** + * Debug-only sanity check for correct attribute domain initialization (order/indices must + * match AttributeDomain). This doesn't check for all possible missuses, but should catch the most + * likely mistakes. + */ +void dataset_layout_hierarchy_sanity_check(const DatasetLayoutHierarchy &hierarchy) +{ + for (const DatasetComponentLayoutInfo &component : hierarchy.components) { + for (uint i = 0; i < component.attr_domains.size(); i++) { + if (component.attr_domains[i]) { + BLI_assert(component.attr_domains[i]->type == static_cast(i)); + } + } + } +} +#endif + +} // namespace blender::ed::spreadsheet diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_layout.hh b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_layout.hh new file mode 100644 index 00000000000..d463739a0fa --- /dev/null +++ b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_layout.hh @@ -0,0 +1,68 @@ +/* + * 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 +#include + +/* Enum definitions... */ +#include "BKE_attribute.h" +#include "BKE_geometry_set.h" + +#include "BLI_span.hh" + +/* More enum definitions... */ +#include "UI_resources.h" + +#pragma once + +namespace blender::ed::spreadsheet { + +struct DatasetAttrDomainLayoutInfo { + AttributeDomain type; + const char *label; + BIFIconID icon; + + constexpr DatasetAttrDomainLayoutInfo(AttributeDomain type, const char *label, BIFIconID icon) + : type(type), label(label), icon(icon) + { + } +}; + +struct DatasetComponentLayoutInfo { + GeometryComponentType type; + const char *label; + BIFIconID icon; + /** Array of attribute-domains. Has to be fixed size based on #AttributeDomain enum, but not all + * values need displaying for all parent components. Hence the optional use. */ + using AttrDomainArray = std::array, ATTR_DOMAIN_NUM>; + const AttrDomainArray attr_domains; +}; + +struct DatasetLayoutHierarchy { + /** The components for display (with layout info like icon and label). Each component stores + * the attribute domains it wants to display (also with layout info like icon and label). */ + const Span components; +}; + +DatasetLayoutHierarchy dataset_layout_hierarchy(); + +#ifndef NDEBUG +void dataset_layout_hierarchy_sanity_check(const DatasetLayoutHierarchy &hierarchy); +#endif + +} // namespace blender::ed::spreadsheet diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh b/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh index 7e3b79a6706..8be5283fd63 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh @@ -16,10 +16,23 @@ #pragma once +#include "BKE_geometry_set.hh" + typedef struct SpaceSpreadsheet_Runtime { int visible_rows; int tot_rows; int tot_columns; } SpaceSpreadsheet_Runtime; +struct bContext; + void spreadsheet_operatortypes(void); +void spreadsheet_update_context_path(const bContext *C); +Object *spreadsheet_get_object_eval(const SpaceSpreadsheet *sspreadsheet, + const Depsgraph *depsgraph); + +namespace blender::ed::spreadsheet { +GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *sspreadsheet, + Object *object_eval, + const GeometryComponentType used_component_type); +} diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_ops.cc b/source/blender/editors/space_spreadsheet/spreadsheet_ops.cc index fcbc37346e6..ceb4cd84cde 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_ops.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_ops.cc @@ -13,6 +13,17 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "BKE_screen.h" + +#include "DNA_space_types.h" + +#include "ED_screen.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" #include "BLI_listbase.h" @@ -89,8 +100,50 @@ static void SPREADSHEET_OT_remove_row_filter_rule(wmOperatorType *ot) RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, INT_MAX); } +static int select_component_domain_invoke(bContext *C, + wmOperator *op, + const wmEvent *UNUSED(event)) +{ + GeometryComponentType component_type = static_cast( + RNA_int_get(op->ptr, "component_type")); + AttributeDomain attribute_domain = static_cast( + RNA_int_get(op->ptr, "attribute_domain_type")); + + SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); + sspreadsheet->geometry_component_type = component_type; + sspreadsheet->attribute_domain = attribute_domain; + + /* Refresh header and main region. */ + WM_main_add_notifier(NC_SPACE | ND_SPACE_SPREADSHEET, NULL); + + return OPERATOR_FINISHED; +} + +static void SPREADSHEET_OT_change_spreadsheet_data_source(wmOperatorType *ot) +{ + ot->name = "Change Visible Data Source"; + ot->description = "Change visible data source in the spreadsheet"; + ot->idname = "SPREADSHEET_OT_change_spreadsheet_data_source"; + + ot->invoke = select_component_domain_invoke; + + RNA_def_int(ot->srna, "component_type", 0, 0, INT16_MAX, "Component Type", "", 0, INT16_MAX); + RNA_def_int(ot->srna, + "attribute_domain_type", + 0, + 0, + INT16_MAX, + "Attribute Domain Type", + "", + 0, + INT16_MAX); + + ot->flag = OPTYPE_INTERNAL; +} + void spreadsheet_operatortypes() { WM_operatortype_append(SPREADSHEET_OT_add_row_filter_rule); WM_operatortype_append(SPREADSHEET_OT_remove_row_filter_rule); + WM_operatortype_append(SPREADSHEET_OT_change_spreadsheet_data_source); } -- cgit v1.2.3