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
This commit is contained in:
Fabian Schempp 2021-06-25 07:57:24 +02:00
parent 841df831e8
commit ae085e301c
Notes: blender-bot 2023-02-14 02:22:07 +01:00
Referenced by issue #89498, Blender file view- can not traverse through directory structure or open files in icon (thumbnail) view
Referenced by issue #89449, UI: File Browser Preview Image not responsive to click
Referenced by issue #86392, Dataset region for spreadsheet editor
30 changed files with 1054 additions and 53 deletions

View File

@ -1041,6 +1041,11 @@ const bTheme U_theme_default = {
.active_object = RGBA(0xffaf29ff),
.edited_object = RGBA(0x00806266),
.row_alternate = RGBA(0xffffff07),
.list = RGBA(0x424242ff),
.list_title = RGBA(0xc3c3c3ff),
.list_text = RGBA(0xc3c3c3ff),
.list_text_hi = RGBA(0xffffff),
.hilite = RGBA(0x80808080),
},
.tarm = {
{

View File

@ -187,7 +187,7 @@ def _template_items_context_panel(menu, key_args_primary):
]
def _template_space_region_type_toggle(*, toolbar_key=None, sidebar_key=None):
def _template_space_region_type_toggle(*, toolbar_key=None, sidebar_key=None, channels_key=None):
items = []
if toolbar_key is not None:
items.append(
@ -199,6 +199,12 @@ def _template_space_region_type_toggle(*, toolbar_key=None, sidebar_key=None):
("wm.context_toggle", sidebar_key,
{"properties": [("data_path", 'space_data.show_region_ui')]}),
)
if channels_key is not None:
items.append(
("wm.context_toggle", channels_key,
{"properties": [("data_path", 'space_data.show_region_channels')]}),
)
return items
@ -3021,6 +3027,7 @@ def km_spreadsheet_generic(_params):
items.extend([
*_template_space_region_type_toggle(
sidebar_key={"type": 'N', "value": 'PRESS'},
channels_key={"type": 'T', "value": 'PRESS'},
),
])

View File

@ -2143,6 +2143,21 @@ 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(
channels_key={"type": 'T', "value": 'PRESS'},
),
])
return keymap
# ------------------------------------------------------------------------------
# Animation

View File

@ -41,10 +41,6 @@ class SPREADSHEET_HT_header(bpy.types.Header):
return
layout.prop(space, "object_eval_state", text="")
if space.object_eval_state != 'ORIGINAL':
layout.prop(space, "geometry_component_type", text="")
if space.geometry_component_type != 'INSTANCES':
layout.prop(space, "attribute_domain", text="")
context_path = space.context_path
if space.object_eval_state == 'ORIGINAL':

View File

@ -38,12 +38,16 @@ struct ID;
struct ReportList;
/* Attribute.domain */
/**
* \warning: Careful when changing existing items. Arrays may be initialized from this (e.g.
* #DATASET_layout_hierarchy).
*/
typedef enum AttributeDomain {
ATTR_DOMAIN_AUTO = -1, /* Use for nodes to choose automatically based on other data. */
ATTR_DOMAIN_POINT = 0, /* Mesh, Hair or PointCloud Point */
ATTR_DOMAIN_EDGE = 1, /* Mesh Edge */
ATTR_DOMAIN_CORNER = 2, /* Mesh Corner */
ATTR_DOMAIN_FACE = 3, /* Mesh Face */
ATTR_DOMAIN_FACE = 2, /* Mesh Face */
ATTR_DOMAIN_CORNER = 3, /* Mesh Corner */
ATTR_DOMAIN_CURVE = 4, /* Hair Curve */
ATTR_DOMAIN_NUM

View File

@ -39,7 +39,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 4
#define BLENDER_FILE_SUBVERSION 5
/* 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

@ -100,7 +100,7 @@ size_t BLI_str_format_int_grouped(char dst[16], int num) ATTR_NONNULL();
size_t BLI_str_format_uint64_grouped(char dst[16], uint64_t num) ATTR_NONNULL();
void BLI_str_format_byte_unit(char dst[15], long long int bytes, const bool base_10)
ATTR_NONNULL();
void BLI_str_format_attribute_domain_size(char dst[4], int number_to_format) ATTR_NONNULL();
int BLI_strcaseeq(const char *a, const char *b) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
char *BLI_strcasestr(const char *s, const char *find) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
char *BLI_strncasestr(const char *s, const char *find, size_t len) ATTR_WARN_UNUSED_RESULT

View File

@ -1229,6 +1229,44 @@ void BLI_str_format_byte_unit(char dst[15], long long int bytes, const bool base
BLI_strncpy(dst + len, base_10 ? units_base_10[order] : units_base_2[order], dst_len - len);
}
/**
* Format a attribute domain to a up to 6 places (plus '\0' terminator) string using long number names abbreviations.
* This function is designed to produce a compact representation of large numbers.
* 1 -> 1
* 15 -> 15
* 155 -> 155
* 1555 -> 1.6K
* 15555 -> 15.6K
* 155555 -> 156K
* 1555555 -> 1.6M
* 15555555 -> 15.6M
* 155555555 -> 156M
* 1000000000 -> 1B
* ...
* Dimension of 7 is the maximum length of the resulting string
* A combination with 7 places would be -15.5K\0
*/
void BLI_str_format_attribute_domain_size(char dst[7], int number_to_format)
{
float number_to_format_converted = number_to_format;
int order = 0;
const float base = 1000;
const char *units[] = {"", "K", "M", "B"};
const int tot_units = ARRAY_SIZE(units);
while ((fabsf(number_to_format_converted) >= base) && ((order + 1) < tot_units)) {
number_to_format_converted /= base;
order++;
}
const size_t dst_len = 7;
int decimals = 0;
if ((order > 0) && fabsf(number_to_format_converted) < 100.0f) {
decimals = 1;
}
BLI_snprintf(dst, dst_len, "%.*f%s", decimals, number_to_format_converted, units[order]);
}
/**
* Find the ranges needed to split \a str into its individual words.
*

View File

@ -420,6 +420,101 @@ TEST(string, StrFormatByteUnits)
EXPECT_STREQ("-8191.8472 PiB", size_str);
}
/* BLI_str_format_attribute_domain_size */
TEST(string, StrFormatAttributeDomainSize)
{
char size_str[7];
int size;
BLI_str_format_attribute_domain_size(size_str, size = 0);
EXPECT_STREQ("0", size_str);
BLI_str_format_attribute_domain_size(size_str, size = 1);
EXPECT_STREQ("1", size_str);
BLI_str_format_attribute_domain_size(size_str, size = 10);
EXPECT_STREQ("10", size_str);
BLI_str_format_attribute_domain_size(size_str, size = 15);
EXPECT_STREQ("15", size_str);
BLI_str_format_attribute_domain_size(size_str, size = 100);
EXPECT_STREQ("100", size_str);
BLI_str_format_attribute_domain_size(size_str, size = 155);
EXPECT_STREQ("155", size_str);
BLI_str_format_attribute_domain_size(size_str, size = 1000);
EXPECT_STREQ("1.0K", size_str);
BLI_str_format_attribute_domain_size(size_str, size = 1555);
EXPECT_STREQ("1.6K", size_str);
BLI_str_format_attribute_domain_size(size_str, size = 10000);
EXPECT_STREQ("10.0K", size_str);
BLI_str_format_attribute_domain_size(size_str, size = 15555);
EXPECT_STREQ("15.6K", size_str);
BLI_str_format_attribute_domain_size(size_str, size = 100000);
EXPECT_STREQ("100K", size_str);
BLI_str_format_attribute_domain_size(size_str, size = 100000);
EXPECT_STREQ("100K", size_str);
BLI_str_format_attribute_domain_size(size_str, size = 155555);
EXPECT_STREQ("156K", size_str);
BLI_str_format_attribute_domain_size(size_str, size = 1000000);
EXPECT_STREQ("1.0M", size_str);
BLI_str_format_attribute_domain_size(size_str, size = 1555555);
EXPECT_STREQ("1.6M", size_str);
BLI_str_format_attribute_domain_size(size_str, size = 10000000);
EXPECT_STREQ("10.0M", size_str);
BLI_str_format_attribute_domain_size(size_str, size = 15555555);
EXPECT_STREQ("15.6M", size_str);
BLI_str_format_attribute_domain_size(size_str, size = 100000000);
EXPECT_STREQ("100M", size_str);
BLI_str_format_attribute_domain_size(size_str, size = 155555555);
EXPECT_STREQ("156M", size_str);
BLI_str_format_attribute_domain_size(size_str, size = 1000000000);
EXPECT_STREQ("1.0B", size_str);
/* Largest possible value. */
BLI_str_format_attribute_domain_size(size_str, size = INT32_MAX);
EXPECT_STREQ("2.1B", size_str);
BLI_str_format_attribute_domain_size(size_str, size = -0);
EXPECT_STREQ("0", size_str);
BLI_str_format_attribute_domain_size(size_str, size = -1);
EXPECT_STREQ("-1", size_str);
BLI_str_format_attribute_domain_size(size_str, size = -10);
EXPECT_STREQ("-10", size_str);
BLI_str_format_attribute_domain_size(size_str, size = -15);
EXPECT_STREQ("-15", size_str);
BLI_str_format_attribute_domain_size(size_str, size = -100);
EXPECT_STREQ("-100", size_str);
BLI_str_format_attribute_domain_size(size_str, size = -155);
EXPECT_STREQ("-155", size_str);
BLI_str_format_attribute_domain_size(size_str, size = -1000);
EXPECT_STREQ("-1.0K", size_str);
BLI_str_format_attribute_domain_size(size_str, size = -1555);
EXPECT_STREQ("-1.6K", size_str);
BLI_str_format_attribute_domain_size(size_str, size = -10000);
EXPECT_STREQ("-10.0K", size_str);
BLI_str_format_attribute_domain_size(size_str, size = -15555);
EXPECT_STREQ("-15.6K", size_str);
BLI_str_format_attribute_domain_size(size_str, size = -100000);
EXPECT_STREQ("-100K", size_str);
BLI_str_format_attribute_domain_size(size_str, size = -155555);
EXPECT_STREQ("-156K", size_str);
BLI_str_format_attribute_domain_size(size_str, size = -1000000);
EXPECT_STREQ("-1.0M", size_str);
BLI_str_format_attribute_domain_size(size_str, size = -1555555);
EXPECT_STREQ("-1.6M", size_str);
BLI_str_format_attribute_domain_size(size_str, size = -10000000);
EXPECT_STREQ("-10.0M", size_str);
BLI_str_format_attribute_domain_size(size_str, size = -15555555);
EXPECT_STREQ("-15.6M", size_str);
BLI_str_format_attribute_domain_size(size_str, size = -100000000);
EXPECT_STREQ("-100M", size_str);
BLI_str_format_attribute_domain_size(size_str, size = -155555555);
EXPECT_STREQ("-156M", size_str);
BLI_str_format_attribute_domain_size(size_str, size = -1000000000);
EXPECT_STREQ("-1.0B", size_str);
/* Smallest possible value. */
BLI_str_format_attribute_domain_size(size_str, size = -INT32_MAX);
EXPECT_STREQ("-2.1B", size_str);
}
struct WordInfo {
WordInfo() = default;
WordInfo(int start, int end) : start(start), end(end)

View File

@ -40,7 +40,9 @@
#include "BKE_node.h"
#include "BLO_readfile.h"
#include "MEM_guardedalloc.h"
#include "readfile.h"
#include "versioning_common.h"
#include "MEM_guardedalloc.h"
@ -122,6 +124,7 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports))
sort_linked_ids(bmain);
assert_sorted_ids(bmain);
}
if (MAIN_VERSION_ATLEAST(bmain, 300, 3)) {
assert_sorted_ids(bmain);
}
@ -392,6 +395,26 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 5)) {
/* Add a dataset 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 *spreadsheet_dataset_region = do_versions_add_region_if_not_found(
regionbase, RGN_TYPE_CHANNELS, "spreadsheet dataset region", RGN_TYPE_FOOTER);
if (spreadsheet_dataset_region) {
spreadsheet_dataset_region->alignment = RGN_ALIGN_LEFT;
spreadsheet_dataset_region->v2d.scroll = (V2D_SCROLL_RIGHT | V2D_SCROLL_BOTTOM);
}
}
}
}
}
}
/**
* Versioning code until next subversion bump goes here.
*

View File

@ -277,6 +277,15 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme)
btheme->space_spreadsheet = btheme->space_outliner;
}
if (!USER_VERSION_ATLEAST(300, 5)) {
FROM_DEFAULT_V4_UCHAR(space_spreadsheet.active);
FROM_DEFAULT_V4_UCHAR(space_spreadsheet.list);
FROM_DEFAULT_V4_UCHAR(space_spreadsheet.list_text);
FROM_DEFAULT_V4_UCHAR(space_spreadsheet.list_text_hi);
FROM_DEFAULT_V4_UCHAR(space_spreadsheet.hilite);
FROM_DEFAULT_V4_UCHAR(space_spreadsheet.selected_highlight);
}
/**
* Versioning code until next subversion bump goes here.
*

View File

@ -30,9 +30,9 @@ struct SpreadsheetContext *ED_spreadsheet_context_new(int type);
void ED_spreadsheet_context_free(struct SpreadsheetContext *context);
void ED_spreadsheet_context_path_clear(struct SpaceSpreadsheet *sspreadsheet);
void ED_spreadsheet_context_path_update_tag(struct SpaceSpreadsheet *sspreadsheet);
uint64_t ED_spreadsheet_context_path_hash(struct SpaceSpreadsheet *sspreadsheet);
uint64_t ED_spreadsheet_context_path_hash(const struct SpaceSpreadsheet *sspreadsheet);
struct ID *ED_spreadsheet_get_current_id(struct SpaceSpreadsheet *sspreadsheet);
struct ID *ED_spreadsheet_get_current_id(const struct SpaceSpreadsheet *sspreadsheet);
void ED_spreadsheet_set_geometry_node_context(struct SpaceSpreadsheet *sspreadsheet,
struct SpaceNode *snode,

View File

@ -386,6 +386,7 @@ typedef enum {
/** Resize handle (resize uilist). */
UI_BTYPE_GRIP = 57 << 9,
UI_BTYPE_DECORATOR = 58 << 9,
UI_BTYPE_DATASETROW = 59 << 9,
} eButType;
#define BUTTYPE (63 << 9)
@ -516,7 +517,7 @@ typedef struct ARegion *(*uiButSearchTooltipFn)(struct bContext *C,
/* Must return allocated string. */
typedef char *(*uiButToolTipFunc)(struct bContext *C, void *argN, const char *tip);
typedef int (*uiButPushedStateFunc)(struct bContext *C, void *arg);
typedef int (*uiButPushedStateFunc)(struct uiBut *but, const void *arg);
typedef void (*uiBlockHandleFunc)(struct bContext *C, void *arg, int event);
@ -1612,6 +1613,13 @@ int UI_searchbox_size_x(void);
/* check if a string is in an existing search box */
int UI_search_items_find_index(uiSearchItems *items, const char *name);
void UI_but_hint_drawstr_set(uiBut *but, const char *string);
void UI_but_datasetrow_indentation_set(uiBut *but, int indentation);
void UI_but_datasetrow_component_set(uiBut *but, uint8_t geometry_component_type);
void UI_but_datasetrow_domain_set(uiBut *but, uint8_t attribute_domain);
uint8_t UI_but_datasetrow_component_get(uiBut *but);
uint8_t UI_but_datasetrow_domain_get(uiBut *but);
void UI_but_node_link_set(uiBut *but, struct bNodeSocket *socket, const float draw_color[4]);
void UI_but_number_step_size_set(uiBut *but, float step_size);
@ -1650,7 +1658,7 @@ void UI_but_focus_on_enter_event(struct wmWindow *win, uiBut *but);
void UI_but_func_hold_set(uiBut *but, uiButHandleHoldFunc func, void *argN);
void UI_but_func_pushed_state_set(uiBut *but, uiButPushedStateFunc func, void *arg);
void UI_but_func_pushed_state_set(uiBut *but, uiButPushedStateFunc func, const void *arg);
struct PointerRNA *UI_but_extra_operator_icon_add(uiBut *but,
const char *opname,

View File

@ -2105,8 +2105,10 @@ void UI_region_message_subscribe(ARegion *region, struct wmMsgBus *mbus)
int ui_but_is_pushed_ex(uiBut *but, double *value)
{
int is_push = 0;
if (but->bit) {
if (but->pushed_state_func) {
return but->pushed_state_func(but, but->pushed_state_arg);
}
else if (but->bit) {
const bool state = !ELEM(
but->type, UI_BTYPE_TOGGLE_N, UI_BTYPE_ICON_TOGGLE_N, UI_BTYPE_CHECKBOX_N);
int lvalue;
@ -2253,7 +2255,6 @@ void ui_but_v3_get(uiBut *but, float vec[3])
}
else if (but->pointype == UI_BUT_POIN_CHAR) {
const char *cp = (char *)but->poin;
vec[0] = ((float)cp[0]) / 255.0f;
vec[1] = ((float)cp[1]) / 255.0f;
vec[2] = ((float)cp[2]) / 255.0f;
@ -3903,6 +3904,10 @@ static void ui_but_alloc_info(const eButType type,
alloc_size = sizeof(uiButCurveProfile);
alloc_str = "uiButCurveProfile";
break;
case UI_BTYPE_DATASETROW:
alloc_size = sizeof(uiButDatasetRow);
alloc_str = "uiButDatasetRow";
break;
default:
alloc_size = sizeof(uiBut);
alloc_str = "uiBut";
@ -4100,6 +4105,7 @@ static uiBut *ui_def_but(uiBlock *block,
UI_BTYPE_BUT_MENU,
UI_BTYPE_SEARCH_MENU,
UI_BTYPE_PROGRESS_BAR,
UI_BTYPE_DATASETROW,
UI_BTYPE_POPOVER)) {
but->drawflag |= (UI_BUT_TEXT_LEFT | UI_BUT_ICON_LEFT);
}
@ -6336,10 +6342,11 @@ void UI_but_func_tooltip_set(uiBut *but, uiButToolTipFunc func, void *argN)
but->tip_argN = argN;
}
void UI_but_func_pushed_state_set(uiBut *but, uiButPushedStateFunc func, void *arg)
void UI_but_func_pushed_state_set(uiBut *but, uiButPushedStateFunc func, const void *arg)
{
but->pushed_state_func = func;
but->pushed_state_arg = arg;
ui_but_update(but);
}
uiBut *uiDefBlockBut(uiBlock *block,
@ -6824,6 +6831,56 @@ uiBut *uiDefSearchButO_ptr(uiBlock *block,
return but;
}
void UI_but_datasetrow_indentation_set(uiBut *but, int indentation)
{
uiButDatasetRow *but_dataset = (uiButDatasetRow *)but;
BLI_assert(but->type == UI_BTYPE_DATASETROW);
but_dataset->indentation = indentation;
BLI_assert(indentation >= 0);
}
/**
* Adds a hint to the button which draws right aligned, grayed out and never clipped.
*/
void UI_but_hint_drawstr_set(uiBut *but, const char *string)
{
ui_but_add_shortcut(but, string, false);
}
void UI_but_datasetrow_component_set(uiBut *but, uint8_t geometry_component_type)
{
uiButDatasetRow *but_dataset_row = (uiButDatasetRow *)but;
BLI_assert(but->type == UI_BTYPE_DATASETROW);
but_dataset_row->geometry_component_type = geometry_component_type;
}
void UI_but_datasetrow_domain_set(uiBut *but, uint8_t attribute_domain)
{
uiButDatasetRow *but_dataset_row = (uiButDatasetRow *)but;
BLI_assert(but->type == UI_BTYPE_DATASETROW);
but_dataset_row->attribute_domain = attribute_domain;
}
uint8_t UI_but_datasetrow_component_get(uiBut *but)
{
uiButDatasetRow *but_dataset_row = (uiButDatasetRow *)but;
BLI_assert(but->type == UI_BTYPE_DATASETROW);
return but_dataset_row->geometry_component_type;
}
uint8_t UI_but_datasetrow_domain_get(uiBut *but)
{
uiButDatasetRow *but_dataset_row = (uiButDatasetRow *)but;
BLI_assert(but->type == UI_BTYPE_DATASETROW);
return but_dataset_row->attribute_domain;
}
void UI_but_node_link_set(uiBut *but, bNodeSocket *socket, const float draw_color[4])
{
but->flag |= UI_BUT_NODE_LINK;

View File

@ -1461,12 +1461,9 @@ static bool ui_drag_toggle_but_is_supported(const uiBut *but)
/* Button pushed state to compare if other buttons match. Can be more
* then just true or false for toggle buttons with more than 2 states. */
static int ui_drag_toggle_but_pushed_state(bContext *C, uiBut *but)
static int ui_drag_toggle_but_pushed_state(uiBut *but)
{
if (but->rnapoin.data == NULL && but->poin == NULL && but->icon) {
if (but->pushed_state_func) {
return but->pushed_state_func(C, but->pushed_state_arg);
}
/* Assume icon identifies a unique state, for buttons that
* work through functions callbacks and don't have an boolean
* value that indicates the state. */
@ -1513,7 +1510,7 @@ static bool ui_drag_toggle_set_xy_xy(
/* execute the button */
if (ui_drag_toggle_but_is_supported(but)) {
/* is it pressed? */
const int pushed_state_but = ui_drag_toggle_but_pushed_state(C, but);
const int pushed_state_but = ui_drag_toggle_but_pushed_state(but);
if (pushed_state_but != pushed_state) {
UI_but_execute(C, region, but);
if (do_check) {
@ -1943,7 +1940,7 @@ static bool ui_but_drag_init(bContext *C,
* typically 'button_activate_exit()' handles this */
ui_apply_but_autokey(C, but);
drag_info->pushed_state = ui_drag_toggle_but_pushed_state(C, but);
drag_info->pushed_state = ui_drag_toggle_but_pushed_state(but);
drag_info->but_cent_start[0] = BLI_rctf_cent_x(&but->rect);
drag_info->but_cent_start[1] = BLI_rctf_cent_y(&but->rect);
copy_v2_v2_int(drag_info->xy_init, &event->x);
@ -2187,6 +2184,9 @@ static void ui_apply_but(
case UI_BTYPE_LISTROW:
ui_apply_but_ROW(C, block, but, data);
break;
case UI_BTYPE_DATASETROW:
ui_apply_but_ROW(C, block, but, data);
break;
case UI_BTYPE_TAB:
ui_apply_but_TAB(C, but, data);
break;
@ -7851,6 +7851,9 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
case UI_BTYPE_LABEL:
case UI_BTYPE_IMAGE:
case UI_BTYPE_PROGRESS_BAR:
case UI_BTYPE_DATASETROW:
retval = ui_do_but_TOG(C, but, data, event);
break;
case UI_BTYPE_NODE_SOCKET:
retval = ui_do_but_EXIT(C, but, data, event);
break;

View File

@ -279,7 +279,7 @@ struct uiBut {
float *editvec;
uiButPushedStateFunc pushed_state_func;
void *pushed_state_arg;
const void *pushed_state_arg;
/* pointer back */
uiBlock *block;
@ -350,6 +350,15 @@ typedef struct uiButProgressbar {
float progress;
} uiButProgressbar;
/** Derived struct for #UI_BTYPE_DATASETROW. */
typedef struct uiButDatasetRow {
uiBut but;
uint8_t geometry_component_type;
uint8_t attribute_domain;
int indentation;
} uiButDatasetRow;
/** Derived struct for #UI_BTYPE_HSVCUBE. */
typedef struct uiButHSVCube {
uiBut but;

View File

@ -67,7 +67,8 @@ bool ui_but_is_toggle(const uiBut *but)
UI_BTYPE_TOGGLE_N,
UI_BTYPE_CHECKBOX,
UI_BTYPE_CHECKBOX_N,
UI_BTYPE_ROW);
UI_BTYPE_ROW,
UI_BTYPE_DATASETROW);
}
/**

View File

@ -113,6 +113,7 @@ typedef enum {
UI_WTYPE_LISTITEM,
UI_WTYPE_PROGRESSBAR,
UI_WTYPE_NODESOCKET,
UI_WTYPE_DATASETROW,
} uiWidgetTypeEnum;
/* Button state argument shares bits with 'uiBut.flag'.
@ -3697,6 +3698,28 @@ static void widget_progressbar(
rect->xmax += (BLI_rcti_size_x(&rect_prog) / 2);
}
static void widget_datasetrow(
uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int UNUSED(roundboxalign))
{
uiButDatasetRow *but_componentrow = (uiButDatasetRow *)but;
uiWidgetBase wtb;
widget_init(&wtb);
/* no outline */
wtb.draw_outline = false;
const float rad = wcol->roundness * U.widget_unit;
round_box_edges(&wtb, UI_CNR_ALL, rect, rad);
if ((state & UI_ACTIVE) || (state & UI_SELECT)) {
widgetbase_draw(&wtb, wcol);
}
BLI_rcti_resize(rect,
BLI_rcti_size_x(rect) - UI_UNIT_X * but_componentrow->indentation,
BLI_rcti_size_y(rect));
BLI_rcti_translate(rect, 0.5f * UI_UNIT_X * but_componentrow->indentation, 0);
}
static void widget_nodesocket(
uiBut *but, uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int UNUSED(roundboxalign))
{
@ -4469,6 +4492,10 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type)
wt.custom = widget_progressbar;
break;
case UI_WTYPE_DATASETROW:
wt.custom = widget_datasetrow;
break;
case UI_WTYPE_NODESOCKET:
wt.custom = widget_nodesocket;
break;
@ -4792,6 +4819,11 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu
fstyle = &style->widgetlabel;
break;
case UI_BTYPE_DATASETROW:
wt = widget_type(UI_WTYPE_DATASETROW);
fstyle = &style->widgetlabel;
break;
case UI_BTYPE_SCROLL:
wt = widget_type(UI_WTYPE_SCROLL);
break;

View File

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

View File

@ -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)
}
}
Object *spreadsheet_get_object_eval(const SpaceSpreadsheet *sspreadsheet,
const Depsgraph *depsgraph)
{
ID *used_id = ED_spreadsheet_get_current_id(sspreadsheet);
if (used_id == nullptr) {
return nullptr;
}
const ID_Type id_type = GS(used_id->name);
if (id_type != ID_OB) {
return nullptr;
}
Object *object_orig = (Object *)used_id;
if (!ELEM(object_orig->type, OB_MESH, OB_POINTCLOUD, OB_VOLUME)) {
return nullptr;
}
Object *object_eval = DEG_get_evaluated_object(depsgraph, object_orig);
if (object_eval == nullptr) {
return nullptr;
}
return object_eval;
}
static std::unique_ptr<DataSource> get_data_source(const bContext *C)
{
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 {};
}
const ID_Type id_type = GS(used_id->name);
if (id_type != ID_OB) {
return {};
}
Object *object_orig = (Object *)used_id;
if (!ELEM(object_orig->type, OB_MESH, OB_POINTCLOUD, OB_VOLUME)) {
return {};
}
Object *object_eval = DEG_get_evaluated_object(depsgraph, object_orig);
if (object_eval == nullptr) {
return {};
}
return data_source_from_geometry(C, object_eval);
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<DataSource> 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(&region->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(&region->handlers, keymap);
}
static void spreadsheet_dataset_region_draw(const bContext *C, ARegion *region)
{
spreadsheet_update_context_path(C);
View2D *v2d = &region->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);
}

View File

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

View File

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

View File

@ -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 <array>
#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<int, 2> 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<int, 2> &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<int, 2> &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<MeshComponent>();
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<PointCloudComponent>();
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<InstancesComponent>();
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<VolumeComponent>();
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<CurveComponent>();
return curve_component->attribute_domain_size(domain);
}
return 0;
}
void DatasetRegionDrawer::draw_dataset_row(const int indentation,
const GeometryComponentType component,
const std::optional<AttributeDomain> 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(&region->v2d, region->winx, abs(drawer.ymin_offset));
UI_block_draw(C, block);
}
} // namespace blender::ed::spreadsheet

View File

@ -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 <array>
#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 &block;
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<AttributeDomain> 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

View File

@ -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 <optional>
#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<DatasetAttrDomainLayoutInfo> \
{ \
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<AttributeDomain>(i));
}
}
}
}
#endif
} // namespace blender::ed::spreadsheet

View File

@ -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 <array>
#include <optional>
/* 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<std::optional<DatasetAttrDomainLayoutInfo>, 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<DatasetComponentLayoutInfo> components;
};
DatasetLayoutHierarchy dataset_layout_hierarchy();
#ifndef NDEBUG
void dataset_layout_hierarchy_sanity_check(const DatasetLayoutHierarchy &hierarchy);
#endif
} // namespace blender::ed::spreadsheet

View File

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

View File

@ -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<GeometryComponentType>(
RNA_int_get(op->ptr, "component_type"));
AttributeDomain attribute_domain = static_cast<AttributeDomain>(
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);
}

View File

@ -776,6 +776,20 @@ static void rna_Space_show_region_toolbar_update(bContext *C, PointerRNA *ptr)
rna_Space_bool_from_region_flag_update_by_type(C, ptr, RGN_TYPE_TOOLS, RGN_FLAG_HIDDEN);
}
/* Channels Region. */
static bool rna_Space_show_region_channels_get(PointerRNA *ptr)
{
return !rna_Space_bool_from_region_flag_get_by_type(ptr, RGN_TYPE_CHANNELS, RGN_FLAG_HIDDEN);
}
static void rna_Space_show_region_channels_set(PointerRNA *ptr, bool value)
{
rna_Space_bool_from_region_flag_set_by_type(ptr, RGN_TYPE_CHANNELS, RGN_FLAG_HIDDEN, !value);
}
static void rna_Space_show_region_channels_update(bContext *C, PointerRNA *ptr)
{
rna_Space_bool_from_region_flag_update_by_type(C, ptr, RGN_TYPE_CHANNELS, RGN_FLAG_HIDDEN);
}
/* UI Region */
static bool rna_Space_show_region_ui_get(PointerRNA *ptr)
{
@ -3214,6 +3228,10 @@ static void rna_def_space_generic_show_region_toggles(StructRNA *srna, int regio
region_type_mask &= ~(1 << RGN_TYPE_TOOLS);
DEF_SHOW_REGION_PROPERTY(show_region_toolbar, "Toolbar", "");
}
if (region_type_mask & (1 << RGN_TYPE_CHANNELS)) {
region_type_mask &= ~(1 << RGN_TYPE_CHANNELS);
DEF_SHOW_REGION_PROPERTY(show_region_channels, "Channels", "");
}
if (region_type_mask & (1 << RGN_TYPE_UI)) {
region_type_mask &= ~(1 << RGN_TYPE_UI);
DEF_SHOW_REGION_PROPERTY(show_region_ui, "Sidebar", "");
@ -7560,6 +7578,9 @@ static void rna_def_space_spreadsheet_context(BlenderRNA *brna)
RNA_def_property_enum_items(prop, spreadsheet_context_type_items);
RNA_def_property_ui_text(prop, "Type", "Type of the context");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
rna_def_space_generic_show_region_toggles(srna,
(1 << RGN_TYPE_CHANNELS) | (1 << RGN_TYPE_FOOTER));
}
static void rna_def_space_spreadsheet_context_object(BlenderRNA *brna)
@ -7674,7 +7695,7 @@ 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_UI) | (1 << RGN_TYPE_FOOTER));
rna_def_space_generic_show_region_toggles(srna, (1 << RGN_TYPE_UI) | (1 << RGN_TYPE_CHANNELS) | (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);

View File

@ -3864,6 +3864,7 @@ static void rna_def_userdef_theme_space_spreadsheet(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
rna_def_userdef_theme_spaces_main(srna);
rna_def_userdef_theme_spaces_list_main(srna);
}
static void rna_def_userdef_themes(BlenderRNA *brna)