Blender V5.0
spreadsheet_ops.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include <fmt/format.h>
6
7#include "DNA_array_utils.hh"
8#include "DNA_space_types.h"
9
10#include "ED_screen.hh"
11#include "ED_spreadsheet.hh"
12
13#include "BLI_listbase.h"
14#include "BLI_rect.h"
15
16#include "BKE_context.hh"
17
18#include "RNA_access.hh"
19#include "RNA_define.hh"
20
21#include "UI_interface_c.hh"
22#include "UI_view2d.hh"
23
24#include "WM_api.hh"
25#include "WM_types.hh"
26
27#include "spreadsheet_column.hh"
28#include "spreadsheet_intern.hh"
30
32
34{
36
38 BLI_addtail(&sspreadsheet->row_filters, row_filter);
39
41
42 return OPERATOR_FINISHED;
43}
44
46{
47 ot->name = "Add Row Filter";
48 ot->description = "Add a filter to remove rows from the displayed data";
49 ot->idname = "SPREADSHEET_OT_add_row_filter_rule";
50
51 ot->exec = row_filter_add_exec;
53
55}
56
58{
60
62 &sspreadsheet->row_filters, RNA_int_get(op->ptr, "index"));
63 if (row_filter == nullptr) {
64 return OPERATOR_CANCELLED;
65 }
66
67 BLI_remlink(&sspreadsheet->row_filters, row_filter);
69
71
72 return OPERATOR_FINISHED;
73}
74
76{
77 ot->name = "Remove Row Filter";
78 ot->description = "Remove a row filter from the rules";
79 ot->idname = "SPREADSHEET_OT_remove_row_filter_rule";
80
83
85
86 RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, INT_MAX);
87}
88
90 wmOperator *op,
91 const wmEvent * /*event*/)
92{
93 const auto component_type = bke::GeometryComponent::Type(RNA_int_get(op->ptr, "component_type"));
94 bke::AttrDomain domain = bke::AttrDomain(RNA_int_get(op->ptr, "attribute_domain_type"));
95
97 sspreadsheet->geometry_id.geometry_component_type = uint8_t(component_type);
98 sspreadsheet->geometry_id.attribute_domain = uint8_t(domain);
99
100 /* Refresh header and main region. */
102
103 return OPERATOR_FINISHED;
104}
105
107{
108 ot->name = "Change Visible Data Source";
109 ot->description = "Change visible data source in the spreadsheet";
110 ot->idname = "SPREADSHEET_OT_change_spreadsheet_data_source";
111
114
115 RNA_def_int(ot->srna, "component_type", 0, 0, INT16_MAX, "Component Type", "", 0, INT16_MAX);
116 RNA_def_int(ot->srna,
117 "attribute_domain_type",
118 0,
119 0,
120 INT16_MAX,
121 "Attribute Domain Type",
122 "",
123 0,
124 INT16_MAX);
125
126 ot->flag = OPTYPE_INTERNAL;
127}
128
134
136{
137 ARegion &region = *CTX_wm_region(C);
139
140 SpreadsheetTable &table = *get_active_table(sspreadsheet);
141 ResizeColumnData &data = *static_cast<ResizeColumnData *>(op->customdata);
142
143 auto cancel = [&]() {
144 data.column->width = data.initial_width_px / SPREADSHEET_WIDTH_UNIT;
145 MEM_delete(&data);
146 ED_region_tag_redraw(&region);
147 return OPERATOR_CANCELLED;
148 };
149 auto finish = [&]() {
151 MEM_delete(&data);
152 ED_region_tag_redraw(&region);
153 return OPERATOR_FINISHED;
154 };
155
156 const int2 cursor_re{event->mval[0], event->mval[1]};
157
158 switch (event->type) {
159 case RIGHTMOUSE:
160 case EVT_ESCKEY: {
161 return cancel();
162 }
163 case LEFTMOUSE: {
164 return finish();
165 }
166 case MOUSEMOVE: {
167 const int offset = cursor_re.x - data.initial_cursor_re.x;
168 const float new_width_px = std::max<float>(SPREADSHEET_WIDTH_UNIT,
169 data.initial_width_px + offset);
170 data.column->width = new_width_px / SPREADSHEET_WIDTH_UNIT;
171 ED_region_tag_redraw(&region);
173 }
174 default: {
176 }
177 }
178}
179
180static bool is_hovering_header_row(const SpaceSpreadsheet &sspreadsheet,
181 const ARegion &region,
182 const int2 &cursor_re)
183{
184 const int region_height = BLI_rcti_size_y(&region.winrct);
185 return cursor_re.y >= region_height - sspreadsheet.runtime->top_row_height &&
186 cursor_re.y <= region_height;
187}
188
190 ARegion &region,
191 const int2 &cursor_re)
192{
193 SpreadsheetTable *table = get_active_table(sspreadsheet);
194 if (!table) {
195 return nullptr;
196 }
197 const float cursor_x_view = UI_view2d_region_to_view_x(&region.v2d, cursor_re.x);
198 for (SpreadsheetColumn *column : Span{table->columns, table->num_columns}) {
199 if (column->flag & SPREADSHEET_COLUMN_FLAG_UNAVAILABLE) {
200 continue;
201 }
202 if (std::abs(cursor_x_view - column->runtime->right_x) < SPREADSHEET_EDGE_ACTION_ZONE) {
203 return column;
204 }
205 }
206 return nullptr;
207}
208
210 ARegion &region,
211 const int2 &cursor_re)
212{
213 SpreadsheetTable *table = get_active_table(sspreadsheet);
214 if (!table) {
215 return nullptr;
216 }
217 const float cursor_x_view = UI_view2d_region_to_view_x(&region.v2d, cursor_re.x);
218 for (SpreadsheetColumn *column : Span{table->columns, table->num_columns}) {
219 if (column->flag & SPREADSHEET_COLUMN_FLAG_UNAVAILABLE) {
220 continue;
221 }
222 if (cursor_x_view > column->runtime->left_x && cursor_x_view <= column->runtime->right_x) {
223 return column;
224 }
225 }
226 return nullptr;
227}
228
230 ARegion &region,
231 const int2 &cursor_re)
232{
233 if (!is_hovering_header_row(sspreadsheet, region, cursor_re)) {
234 return nullptr;
235 }
236 return find_hovered_column_edge(sspreadsheet, region, cursor_re);
237}
238
240 ARegion &region,
241 const int2 &cursor_re)
242{
243 if (!is_hovering_header_row(sspreadsheet, region, cursor_re)) {
244 return nullptr;
245 }
246 return find_hovered_column(sspreadsheet, region, cursor_re);
247}
248
250{
251 ARegion &region = *CTX_wm_region(C);
253
254 const int2 cursor_re{event->mval[0], event->mval[1]};
256 sspreadsheet, region, cursor_re);
257 if (!column_to_resize) {
259 }
260
261 ResizeColumnData *data = MEM_new<ResizeColumnData>(__func__);
262 data->column = column_to_resize;
263 data->initial_cursor_re = cursor_re;
264 data->initial_width_px = column_to_resize->width * SPREADSHEET_WIDTH_UNIT;
265 op->customdata = data;
266
269}
270
272{
273 ot->name = "Resize Column";
274 ot->description = "Resize a spreadsheet column";
275 ot->idname = "SPREADSHEET_OT_resize_column";
276
277 ot->invoke = resize_column_invoke;
278 ot->modal = resize_column_modal;
280 ot->flag = OPTYPE_INTERNAL;
281}
282
284{
286 ARegion &region = *CTX_wm_region(C);
287
288 std::unique_ptr<DataSource> data_source = get_data_source(*C);
289 if (!data_source) {
290 return OPERATOR_CANCELLED;
291 }
292 const int2 cursor_re{event->mval[0], event->mval[1]};
293 SpreadsheetColumn *column = find_hovered_column_header_edge(sspreadsheet, region, cursor_re);
294 if (!column) {
296 }
297
298 std::unique_ptr<ColumnValues> values = data_source->get_column_values(*column->id);
299 if (!values) {
300 return OPERATOR_CANCELLED;
301 }
302
303 SpreadsheetTable &table = *get_active_table(sspreadsheet);
305
306 const float width_px = values->fit_column_width_px();
307 column->width = width_px / SPREADSHEET_WIDTH_UNIT;
308
309 ED_region_tag_redraw(&region);
310 return OPERATOR_FINISHED;
311}
312
314{
315 ot->name = "Fit Column";
316 ot->description = "Resize a spreadsheet column to the width of the data";
317 ot->idname = "SPREADSHEET_OT_fit_column";
318
319 ot->invoke = fit_column_invoke;
321 ot->flag = OPTYPE_INTERNAL;
322}
323
329
330static std::optional<int> find_first_available_column_index(const SpreadsheetTable &table)
331{
332 for (int i = 0; i < table.num_columns; i++) {
334 return i;
335 }
336 }
337 return std::nullopt;
338}
339
340static std::optional<int> find_last_available_column_index(const SpreadsheetTable &table)
341{
342 for (int i = table.num_columns - 1; i >= 0; i--) {
344 return i;
345 }
346 }
347 return std::nullopt;
348}
349
351{
353 ARegion &region = *CTX_wm_region(C);
354
355 const int2 cursor_re{event->mval[0], event->mval[1]};
356
357 if (find_hovered_column_edge(sspreadsheet, region, cursor_re)) {
359 }
360
361 SpreadsheetColumn *column_to_move = find_hovered_column_header(sspreadsheet, region, cursor_re);
362 if (!column_to_move) {
364 }
365
367
368 SpreadsheetTable *table = get_active_table(sspreadsheet);
369 const int old_index = Span{table->columns, table->num_columns}.first_index(column_to_move);
370
371 ReorderColumnData *data = MEM_new<ReorderColumnData>(__func__);
372 data->column = column_to_move;
373 data->initial_cursor_x_view = UI_view2d_region_to_view_x(&region.v2d, cursor_re.x);
374 op->customdata = data;
375
376 ReorderColumnVisualizationData &visualization_data =
377 sspreadsheet.runtime->reorder_column_visualization_data.emplace();
378 visualization_data.old_index = old_index;
379 visualization_data.new_index = old_index;
380 visualization_data.current_offset_x_px = 0;
381
382 UI_view2d_edge_pan_init(C, &data->pan_data, 0, 0, 1, 26, 0.5f, 0.0f);
383 /* Limit to horizontal panning. */
384 data->pan_data.limit.xmin = region.v2d.tot.xmin;
385 data->pan_data.limit.xmax = region.v2d.tot.xmax;
386 data->pan_data.limit.ymin = region.v2d.cur.ymin;
387 data->pan_data.limit.ymax = region.v2d.cur.ymax;
388
391}
392
394{
396 ARegion &region = *CTX_wm_region(C);
397
398 const int2 cursor_re{event->mval[0], event->mval[1]};
399 ReorderColumnData &data = *static_cast<ReorderColumnData *>(op->customdata);
400
401 SpreadsheetTable &table = *get_active_table(sspreadsheet);
402 Span<SpreadsheetColumn *> columns(table.columns, table.num_columns);
403
404 const int old_index = columns.first_index(data.column);
405 int new_index = 0;
406
407 SpreadsheetColumn *hovered_column = find_hovered_column(sspreadsheet, region, cursor_re);
408 if (hovered_column) {
409 new_index = columns.first_index(hovered_column);
410 }
411 else {
412 if (cursor_re.x > sspreadsheet.runtime->left_column_width) {
413 new_index = *find_last_available_column_index(table);
414 }
415 else {
416 new_index = *find_first_available_column_index(table);
417 }
418 }
419
420 auto cleanup_on_finish = [&]() {
421 sspreadsheet.runtime->reorder_column_visualization_data.reset();
422 MEM_delete(&data);
423 ED_region_tag_redraw(&region);
425 };
426
427 switch (event->type) {
428 case RIGHTMOUSE:
429 case EVT_ESCKEY: {
430 UI_view2d_edge_pan_cancel(C, &data.pan_data);
431 cleanup_on_finish();
432 return OPERATOR_CANCELLED;
433 }
434 case LEFTMOUSE: {
435 if (old_index != new_index) {
436 dna::array::move_index(table.columns, table.num_columns, old_index, new_index);
437 }
439 cleanup_on_finish();
440 return OPERATOR_FINISHED;
441 }
442 case MOUSEMOVE: {
443 UI_view2d_edge_pan_apply(C, &data.pan_data, event->xy);
444
445 ReorderColumnVisualizationData &visualization_data =
447 visualization_data.new_index = new_index;
448 visualization_data.current_offset_x_px = UI_view2d_region_to_view_x(&region.v2d,
449 cursor_re.x) -
450 data.initial_cursor_x_view;
451 ED_region_tag_redraw(&region);
453 }
454 case WHEELLEFTMOUSE:
455 case WHEELRIGHTMOUSE: {
456 if (BLI_rcti_isect_pt_v(&region.winrct, event->xy)) {
457 /* Support scrolling left and right. */
459 }
461 }
462 default: {
464 }
465 }
466}
467
469{
470 ot->name = "Reorder Columns";
471 ot->description = "Change the order of columns";
472 ot->idname = "SPREADSHEET_OT_reorder_columns";
473
475 ot->invoke = reorder_columns_invoke;
476 ot->modal = reorder_columns_modal;
477 ot->flag = OPTYPE_INTERNAL;
478}
479
489
490} // namespace blender::ed::spreadsheet
wmWindow * CTX_wm_window(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
SpaceSpreadsheet * CTX_wm_space_spreadsheet(const bContext *C)
void * BLI_findlink(const ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:534
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
bool BLI_rcti_isect_pt_v(const struct rcti *rect, const int xy[2])
BLI_INLINE int BLI_rcti_size_y(const struct rcti *rct)
Definition BLI_rect.h:198
@ SPREADSHEET_COLUMN_FLAG_UNAVAILABLE
#define SPREADSHEET_WIDTH_UNIT
@ SPREADSHEET_TABLE_FLAG_MANUALLY_EDITED
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
bool ED_operator_spreadsheet_active(bContext *C)
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:618
#define C
Definition RandGen.cpp:29
void UI_view2d_edge_pan_cancel(bContext *C, View2DEdgePanData *vpd)
void UI_view2d_edge_pan_init(bContext *C, View2DEdgePanData *vpd, float inside_pad, float outside_pad, float speed_ramp, float max_speed, float delay, float zoom_influence)
void UI_view2d_edge_pan_apply(bContext *C, View2DEdgePanData *vpd, const int xy[2]) ATTR_NONNULL(1
float UI_view2d_region_to_view_x(const View2D *v2d, float x)
Definition view2d.cc:1657
@ OPTYPE_INTERNAL
Definition WM_types.hh:202
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define ND_SPACE_SPREADSHEET
Definition WM_types.hh:541
#define NC_SPACE
Definition WM_types.hh:392
BMesh const char void * data
constexpr int64_t first_index(const T &search_value) const
Definition BLI_span.hh:377
#define INT16_MAX
void move_index(T *items, const int items_num, const int from_index, const int to_index)
SpreadsheetRowFilter * spreadsheet_row_filter_new()
static void SPREADSHEET_OT_resize_column(wmOperatorType *ot)
static void SPREADSHEET_OT_change_spreadsheet_data_source(wmOperatorType *ot)
SpreadsheetColumn * find_hovered_column_edge(SpaceSpreadsheet &sspreadsheet, ARegion &region, const int2 &cursor_re)
SpreadsheetColumn * find_hovered_column(SpaceSpreadsheet &sspreadsheet, ARegion &region, const int2 &cursor_re)
static wmOperatorStatus resize_column_modal(bContext *C, wmOperator *op, const wmEvent *event)
static wmOperatorStatus reorder_columns_modal(bContext *C, wmOperator *op, const wmEvent *event)
static wmOperatorStatus reorder_columns_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static std::optional< int > find_first_available_column_index(const SpreadsheetTable &table)
static void SPREADSHEET_OT_fit_column(wmOperatorType *ot)
std::unique_ptr< DataSource > get_data_source(const bContext &C)
static void SPREADSHEET_OT_reorder_columns(wmOperatorType *ot)
static void SPREADSHEET_OT_add_row_filter_rule(wmOperatorType *ot)
static bool is_hovering_header_row(const SpaceSpreadsheet &sspreadsheet, const ARegion &region, const int2 &cursor_re)
SpreadsheetColumn * find_hovered_column_header(SpaceSpreadsheet &sspreadsheet, ARegion &region, const int2 &cursor_re)
static wmOperatorStatus resize_column_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static wmOperatorStatus row_filter_remove_exec(bContext *C, wmOperator *op)
static wmOperatorStatus row_filter_add_exec(bContext *C, wmOperator *)
void spreadsheet_row_filter_free(SpreadsheetRowFilter *row_filter)
SpreadsheetColumn * find_hovered_column_header_edge(SpaceSpreadsheet &sspreadsheet, ARegion &region, const int2 &cursor_re)
SpreadsheetTable * get_active_table(SpaceSpreadsheet &sspreadsheet)
static std::optional< int > find_last_available_column_index(const SpreadsheetTable &table)
static void SPREADSHEET_OT_remove_row_filter_rule(wmOperatorType *ot)
static wmOperatorStatus select_component_domain_invoke(bContext *C, wmOperator *op, const wmEvent *)
static wmOperatorStatus fit_column_invoke(bContext *C, wmOperator *, const wmEvent *event)
VecBase< int32_t, 2 > int2
int RNA_int_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
#define SPREADSHEET_EDGE_ACTION_ZONE
SpreadsheetTableIDGeometry geometry_id
SpaceSpreadsheet_Runtime * runtime
SpreadsheetColumnID * id
SpreadsheetColumn ** columns
std::optional< ReorderColumnVisualizationData > reorder_column_visualization_data
float xmax
float xmin
float ymax
float ymin
wmEventType type
Definition WM_types.hh:757
int xy[2]
Definition WM_types.hh:761
struct PointerRNA * ptr
i
Definition text_draw.cc:230
void WM_cursor_set(wmWindow *win, int curs)
@ WM_CURSOR_DEFAULT
Definition wm_cursors.hh:15
@ WM_CURSOR_HAND_CLOSED
Definition wm_cursors.hh:23
void WM_main_add_notifier(uint type, void *reference)
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
@ RIGHTMOUSE
@ WHEELLEFTMOUSE
@ WHEELRIGHTMOUSE
@ MOUSEMOVE
@ LEFTMOUSE
@ EVT_ESCKEY
wmOperatorType * ot
Definition wm_files.cc:4237
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))