Blender V5.0
spreadsheet_draw.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 "BKE_context.hh"
6
7#include "UI_interface.hh"
8#include "UI_resources.hh"
9#include "UI_view2d.hh"
10
11#include "GPU_immediate.hh"
12#include "GPU_state.hh"
13
14#include "DNA_screen_types.h"
15#include "DNA_space_types.h"
16#include "DNA_userdef_types.h"
17
18#include "BLI_rect.h"
19
20#include "ED_spreadsheet.hh"
21
22#include "spreadsheet_column.hh"
23#include "spreadsheet_draw.hh"
24#include "spreadsheet_intern.hh"
25
26#define CELL_RIGHT_PADDING (2.0f * UI_SCALE_FAC)
27
29
36
38
39void SpreadsheetDrawer::draw_top_row_cell(int /*column_index*/,
40 const CellDrawParams & /*params*/) const
41{
42}
43
45 const CellDrawParams & /*params*/) const
46{
47}
48
50 int /*column_index*/,
51 const CellDrawParams & /*params*/) const
52{
53}
54
55int SpreadsheetDrawer::column_width(int /*column_index*/) const
56{
57 return 5 * UI_UNIT_X;
58}
59
61 const ARegion *region,
62 const SpreadsheetDrawer &drawer)
63{
65 immRectf(pos, 0, region->winy - drawer.top_row_height, drawer.left_column_width, 0);
66}
67
69 const int scroll_offset_y,
70 const ARegion *region,
71 const SpreadsheetDrawer &drawer)
72{
75 BLI_assert(drawer.row_height > 0);
76 const int row_pair_height = drawer.row_height * 2;
77 const int row_top_y = region->winy - drawer.top_row_height - scroll_offset_y % row_pair_height;
78 for (const int i : IndexRange(region->winy / row_pair_height + 1)) {
79 int x_left = 0;
80 int x_right = region->winx;
81 int y_top = row_top_y - i * row_pair_height - drawer.row_height;
82 int y_bottom = y_top - drawer.row_height;
83 y_top = std::min(y_top, region->winy - drawer.top_row_height);
84 y_bottom = std::min(y_bottom, region->winy - drawer.top_row_height);
85 immRectf(pos, x_left, y_top, x_right, y_bottom);
86 }
88}
89
91 const ARegion *region,
92 const SpreadsheetDrawer &drawer)
93{
95 immRectf(pos, 0, region->winy, region->winx, region->winy - drawer.top_row_height);
96}
97
98static void draw_separator_lines(const uint pos,
99 const int scroll_offset_x,
100 const ARegion *region,
101 const SpreadsheetDrawer &drawer)
102{
104
106
107 /* Left column line. */
108 immVertex2f(pos, drawer.left_column_width, region->winy);
110
111 /* Top row line. */
112 immVertex2f(pos, 0, region->winy - drawer.top_row_height);
113 immVertex2f(pos, region->winx, region->winy - drawer.top_row_height);
114
115 /* Column separator lines. */
116 int line_x = drawer.left_column_width - scroll_offset_x;
117 for (const int column_index : IndexRange(drawer.tot_columns)) {
118 const int column_width = drawer.column_width(column_index);
119 line_x += column_width;
120 if (line_x >= drawer.left_column_width) {
121 immVertex2f(pos, line_x, region->winy);
122 immVertex2f(pos, line_x, 0);
123 }
124 }
125 immEnd();
126}
127
128static void get_visible_rows(const SpreadsheetDrawer &drawer,
129 const ARegion *region,
130 const int scroll_offset_y,
131 int *r_first_row,
132 int *r_max_visible_rows)
133{
134 *r_first_row = -scroll_offset_y / drawer.row_height;
135 *r_max_visible_rows = region->winy / drawer.row_height + 1;
136}
137
138static void draw_left_column_content(const int scroll_offset_y,
139 const bContext *C,
140 ARegion *region,
141 const SpreadsheetDrawer &drawer)
142{
143 int old_scissor[4];
144 GPU_scissor_get(old_scissor);
145
146 GPU_scissor(0, 0, drawer.left_column_width, region->winy - drawer.top_row_height);
147
148 uiBlock *left_column_block = UI_block_begin(C, region, __func__, ui::EmbossType::None);
149 int first_row, max_visible_rows;
150 get_visible_rows(drawer, region, scroll_offset_y, &first_row, &max_visible_rows);
151 for (const int row_index : IndexRange(first_row, max_visible_rows)) {
152 if (row_index >= drawer.tot_rows) {
153 break;
154 }
156 params.block = left_column_block;
157 params.xmin = 0;
158 params.ymin = region->winy - drawer.top_row_height - (row_index + 1) * drawer.row_height -
159 scroll_offset_y;
161 params.height = drawer.row_height;
162 drawer.draw_left_column_cell(row_index, params);
163 }
164
165 UI_block_end(C, left_column_block);
166 UI_block_draw(C, left_column_block);
167
168 GPU_scissor(UNPACK4(old_scissor));
169}
170
171static void draw_top_row_content(const bContext *C,
172 ARegion *region,
173 const SpreadsheetDrawer &drawer,
174 const int scroll_offset_x)
175{
176 int old_scissor[4];
177 GPU_scissor_get(old_scissor);
178
180 region->winy - drawer.top_row_height,
181 region->winx - drawer.left_column_width,
182 drawer.top_row_height);
183
184 uiBlock *first_row_block = UI_block_begin(C, region, __func__, ui::EmbossType::None);
185
186 int left_x = drawer.left_column_width - scroll_offset_x;
187 for (const int column_index : IndexRange(drawer.tot_columns)) {
188 const int column_width = drawer.column_width(column_index);
189 const int right_x = left_x + column_width;
190
192 params.block = first_row_block;
193 params.xmin = left_x;
194 params.ymin = region->winy - drawer.top_row_height;
195 params.width = column_width - CELL_RIGHT_PADDING;
196 params.height = drawer.top_row_height;
197 drawer.draw_top_row_cell(column_index, params);
198
199 left_x = right_x;
200 }
201
202 UI_block_end(C, first_row_block);
203 UI_block_draw(C, first_row_block);
204
205 GPU_scissor(UNPACK4(old_scissor));
206}
207
208static void draw_cell_contents(const bContext *C,
209 ARegion *region,
210 const SpreadsheetDrawer &drawer,
211 const int scroll_offset_x,
212 const int scroll_offset_y)
213{
214 int old_scissor[4];
215 GPU_scissor_get(old_scissor);
216
218 0,
219 region->winx - drawer.left_column_width,
220 region->winy - drawer.top_row_height);
221
222 uiBlock *cells_block = UI_block_begin(C, region, __func__, ui::EmbossType::None);
223
224 int first_row, max_visible_rows;
225 get_visible_rows(drawer, region, scroll_offset_y, &first_row, &max_visible_rows);
226
227 int left_x = drawer.left_column_width - scroll_offset_x;
228 for (const int column_index : IndexRange(drawer.tot_columns)) {
229 const int column_width = drawer.column_width(column_index);
230 const int right_x = left_x + column_width;
231
232 if (right_x >= drawer.left_column_width && left_x <= region->winx) {
233 for (const int row_index : IndexRange(first_row, max_visible_rows)) {
234 if (row_index >= drawer.tot_rows) {
235 break;
236 }
237
239 params.block = cells_block;
240 params.xmin = left_x;
241 params.ymin = region->winy - drawer.top_row_height - (row_index + 1) * drawer.row_height -
242 scroll_offset_y;
243 params.width = column_width - CELL_RIGHT_PADDING;
244 params.height = drawer.row_height;
245 drawer.draw_content_cell(row_index, column_index, params);
246 }
247 }
248
249 left_x = right_x;
250 }
251
252 UI_block_end(C, cells_block);
253 UI_block_draw(C, cells_block);
254
255 GPU_scissor(UNPACK4(old_scissor));
256}
257
259 ARegion *region,
260 const int row_amount)
261{
262 int column_width_sum = 0;
263 for (const int column_index : IndexRange(drawer.tot_columns)) {
264 column_width_sum += drawer.column_width(column_index);
265 }
266 /* Adding some padding avoids issues where the right most column overlaps with other region
267 * elements like its border or the icon to open the sidebar. */
268 const int right_padding = UI_UNIT_X * 0.5f;
269
270 UI_view2d_totRect_set(&region->v2d,
271 column_width_sum + drawer.left_column_width + right_padding,
272 row_amount * drawer.row_height + drawer.top_row_height);
273}
274
276 const ARegion &region,
277 const SpaceSpreadsheet &sspreadsheet,
278 const int scroll_offset_x)
279{
282 const SpreadsheetTable &table = *get_active_table(sspreadsheet);
283 const SpreadsheetColumn &moving_column = *table.columns[data.old_index];
284
285 rctf rect;
286 rect.xmin = moving_column.runtime->left_x - scroll_offset_x;
287 rect.xmax = moving_column.runtime->right_x - scroll_offset_x;
288 rect.ymin = 0;
289 rect.ymax = region.winy;
290
293 immRectf(pos, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
295}
296
297static void draw_column_reorder_destination(const ARegion &region,
298 const SpaceSpreadsheet &sspreadsheet,
299 const SpreadsheetDrawer &drawer,
300 const int scroll_offset_x)
301{
304 const SpreadsheetTable &table = *get_active_table(sspreadsheet);
305 const SpreadsheetColumn &moving_column = *table.columns[data.old_index];
306 const SpreadsheetColumn &insert_column = *table.columns[data.new_index];
307
308 {
309 /* Draw column that is moved. */
312 color.a = 0.3f;
313 rctf offset_column_rect;
314 offset_column_rect.xmin = moving_column.runtime->left_x + data.current_offset_x_px -
315 scroll_offset_x;
316 offset_column_rect.xmax = offset_column_rect.xmin +
317 moving_column.width * SPREADSHEET_WIDTH_UNIT;
318 offset_column_rect.ymin = 0;
319 offset_column_rect.ymax = region.winy;
320 UI_draw_roundbox_4fv(&offset_column_rect, true, 0, color);
321 }
322 {
323 /* Draw indicator where the column is inserted. */
326 color.a = 0.6f;
327 const int insert_column_x = data.new_index <= data.old_index ? insert_column.runtime->left_x :
328 insert_column.runtime->right_x;
329 const int width = UI_UNIT_X * 0.1f;
330 rctf insert_rect;
331 insert_rect.xmin = insert_column_x - width / 2 - scroll_offset_x;
332 insert_rect.xmax = insert_rect.xmin + width;
333 insert_rect.ymin = 0;
334 insert_rect.ymax = region.winy;
335
336 /* Don't draw on top of index column. */
337 const int left_bound = drawer.left_column_width - width / 2;
338 insert_rect.xmin = std::max<float>(insert_rect.xmin, left_bound);
339 insert_rect.xmax = std::max<float>(insert_rect.xmax, left_bound);
340
341 UI_draw_roundbox_4fv(&insert_rect, true, 0, color);
342 }
343}
344
346 ARegion *region,
347 const SpreadsheetDrawer &drawer)
348{
350
351 update_view2d_tot_rect(drawer, region, drawer.tot_rows);
352
354
355 View2D *v2d = &region->v2d;
356 const int scroll_offset_y = v2d->cur.ymax;
357 const int scroll_offset_x = v2d->cur.xmin;
358 bool is_reordering_columns = sspreadsheet.runtime->reorder_column_visualization_data.has_value();
359
361 uint pos = GPU_vertformat_attr_add(format, "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
363
364 draw_index_column_background(pos, region, drawer);
365 draw_alternating_row_overlay(pos, scroll_offset_y, region, drawer);
366 draw_top_row_background(pos, region, drawer);
367 if (is_reordering_columns) {
368 draw_column_reorder_source(pos, *region, sspreadsheet, scroll_offset_x);
369 }
370 draw_separator_lines(pos, scroll_offset_x, region, drawer);
371
373
374 draw_left_column_content(scroll_offset_y, C, region, drawer);
375 draw_top_row_content(C, region, drawer, scroll_offset_x);
376 draw_cell_contents(C, region, drawer, scroll_offset_x, scroll_offset_y);
377
378 if (is_reordering_columns) {
379 draw_column_reorder_destination(*region, sspreadsheet, drawer, scroll_offset_x);
380 }
381
382 rcti scroller_mask;
383 BLI_rcti_init(&scroller_mask,
384 drawer.left_column_width,
385 region->winx,
386 0,
387 region->winy - drawer.top_row_height);
388 UI_view2d_scrollers_draw(v2d, &scroller_mask);
389}
390
391} // namespace blender::ed::spreadsheet
SpaceSpreadsheet * CTX_wm_space_spreadsheet(const bContext *C)
#define BLI_assert(a)
Definition BLI_assert.h:46
void BLI_rcti_init(struct rcti *rect, int xmin, int xmax, int ymin, int ymax)
Definition rct.cc:414
unsigned int uint
#define UNPACK4(a)
#define SPREADSHEET_WIDTH_UNIT
void immEnd()
void immUnbindProgram()
void immBindBuiltinProgram(GPUBuiltinShader shader_id)
void immUniformThemeColorShadeAlpha(int color_id, int color_offset, int alpha_offset)
void immVertex2f(uint attr_id, float x, float y)
void immUniformThemeColor(int color_id)
void immUniformThemeColorShade(int color_id, int offset)
void immBeginAtMost(GPUPrimType, uint max_vertex_len)
GPUVertFormat * immVertexFormat()
void immRectf(uint pos, float x1, float y1, float x2, float y2)
@ GPU_PRIM_LINES
@ GPU_SHADER_3D_UNIFORM_COLOR
@ GPU_BLEND_NONE
Definition GPU_state.hh:85
@ GPU_BLEND_ALPHA
Definition GPU_state.hh:87
void GPU_blend(GPUBlend blend)
Definition gpu_state.cc:42
void GPU_scissor(int x, int y, int width, int height)
Definition gpu_state.cc:193
void GPU_scissor_get(int coords[4])
Definition gpu_state.cc:268
uint GPU_vertformat_attr_add(GPUVertFormat *format, blender::StringRef name, blender::gpu::VertAttrType type)
#define C
Definition RandGen.cpp:29
#define UI_UNIT_Y
void UI_draw_roundbox_4fv(const rctf *rect, bool filled, float rad, const float col[4])
uiBlock * UI_block_begin(const bContext *C, ARegion *region, std::string name, blender::ui::EmbossType emboss)
void UI_block_draw(const bContext *C, uiBlock *block)
#define UI_UNIT_X
void UI_block_end(const bContext *C, uiBlock *block)
@ TH_ROW_ALTERNATE
@ TH_BACK
@ TH_TEXT
void UI_ThemeClearColor(int colorid)
void UI_GetThemeColorShade4fv(int colorid, int offset, float col[4])
void UI_view2d_scrollers_draw(View2D *v2d, const rcti *mask_custom)
Definition view2d.cc:1504
void UI_view2d_totRect_set(View2D *v2d, int width, int height)
Definition view2d.cc:1036
BMesh const char void * data
virtual void draw_top_row_cell(int column_index, const CellDrawParams &params) const
virtual int column_width(int column_index) const
virtual void draw_left_column_cell(int row_index, const CellDrawParams &params) const
virtual void draw_content_cell(int row_index, int column_index, const CellDrawParams &params) const
uint pos
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
format
static void draw_index_column_background(const uint pos, const ARegion *region, const SpreadsheetDrawer &drawer)
static void draw_left_column_content(const int scroll_offset_y, const bContext *C, ARegion *region, const SpreadsheetDrawer &drawer)
static void get_visible_rows(const SpreadsheetDrawer &drawer, const ARegion *region, const int scroll_offset_y, int *r_first_row, int *r_max_visible_rows)
void draw_spreadsheet_in_region(const bContext *C, ARegion *region, const SpreadsheetDrawer &drawer)
static void draw_column_reorder_source(const uint pos, const ARegion &region, const SpaceSpreadsheet &sspreadsheet, const int scroll_offset_x)
static void draw_column_reorder_destination(const ARegion &region, const SpaceSpreadsheet &sspreadsheet, const SpreadsheetDrawer &drawer, const int scroll_offset_x)
static void update_view2d_tot_rect(const SpreadsheetDrawer &drawer, ARegion *region, const int row_amount)
static void draw_top_row_content(const bContext *C, ARegion *region, const SpreadsheetDrawer &drawer, const int scroll_offset_x)
static void draw_alternating_row_overlay(const uint pos, const int scroll_offset_y, const ARegion *region, const SpreadsheetDrawer &drawer)
static void draw_cell_contents(const bContext *C, ARegion *region, const SpreadsheetDrawer &drawer, const int scroll_offset_x, const int scroll_offset_y)
static void draw_separator_lines(const uint pos, const int scroll_offset_x, const ARegion *region, const SpreadsheetDrawer &drawer)
SpreadsheetTable * get_active_table(SpaceSpreadsheet &sspreadsheet)
static void draw_top_row_background(const uint pos, const ARegion *region, const SpreadsheetDrawer &drawer)
ColorTheme4< float > ColorTheme4f
#define CELL_RIGHT_PADDING
SpaceSpreadsheet_Runtime * runtime
SpreadsheetColumnRuntime * runtime
SpreadsheetColumn ** columns
std::optional< ReorderColumnVisualizationData > reorder_column_visualization_data
float xmax
float xmin
float ymax
float ymin
i
Definition text_draw.cc:230