Blender V4.3
interface_button_sections.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
13#include "BLI_span.hh"
14#include "BLI_vector.hh"
15
16#include "DNA_screen_types.h"
17
18#include "GPU_immediate.hh"
19
20#include "interface_intern.hh"
21
22using namespace blender;
23
33static Vector<rcti> button_section_bounds_calc(const ARegion *region, const bool add_padding)
34{
35 Vector<rcti> section_bounds;
36
37 const auto finish_section_fn = [&](const rcti cur_section_bounds) {
38 if (!section_bounds.is_empty() &&
39 std::abs(section_bounds.last().xmax - cur_section_bounds.xmin) <
41 {
42 section_bounds.last().xmax = cur_section_bounds.xmax;
43 }
44 else {
45 section_bounds.append(cur_section_bounds);
46 }
47
48 rcti &last_bounds = section_bounds.last();
49 /* Extend to region edge if close enough. */
50 if (last_bounds.xmin <= UI_BUTTON_SECTION_MERGE_DISTANCE) {
51 last_bounds.xmin = 0;
52 }
53 if (last_bounds.xmax >= (region->winx - UI_BUTTON_SECTION_MERGE_DISTANCE)) {
54 last_bounds.xmax = region->winx;
55 }
56 };
57
58 {
59 bool has_section_content = false;
60 rcti cur_section_bounds;
61 BLI_rcti_init_minmax(&cur_section_bounds);
62
63 /* A bit annoying, but this function is called for both drawing and event handling. When
64 * drawing, we need to exclude inactive blocks since they mess with the result. However, this
65 * active state is only useful during drawing and must be ignored for handling (at which point
66 * #uiBlock::active is false for all blocks). */
67 const bool is_drawing = region->do_draw & RGN_DRAWING;
68 LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
69 if (is_drawing && !block->active) {
70 continue;
71 }
72
73 LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
74 if (but->type == UI_BTYPE_SEPR_SPACER) {
75 /* Start a new section. */
76 if (has_section_content) {
77 finish_section_fn(cur_section_bounds);
78
79 /* Reset for next section. */
80 BLI_rcti_init_minmax(&cur_section_bounds);
81 has_section_content = false;
82 }
83 continue;
84 }
85
86 rcti but_pixelrect;
87 ui_but_to_pixelrect(&but_pixelrect, region, block, but);
88 BLI_rcti_do_minmax_rcti(&cur_section_bounds, &but_pixelrect);
89 has_section_content = true;
90 }
91 }
92
93 /* Finish last section in case the last button is not a spacer. */
94 if (has_section_content) {
95 finish_section_fn(cur_section_bounds);
96 }
97 }
98
99 if (add_padding) {
100 const uiStyle *style = UI_style_get_dpi();
101 const int pad_x = style->buttonspacex;
102 /* Making this based on the header size since this feature is typically used in headers, and
103 * this way we are more likely to pad the bounds all the way to the region edge. */
104 const int pad_y = ceil((HEADER_PADDING_Y * UI_SCALE_FAC) / 2.0f);
105
106 for (rcti &bounds : section_bounds) {
107 BLI_rcti_pad(&bounds, pad_x, pad_y);
108 /* Clamp, important for the rounded-corners to draw correct. */
109 CLAMP_MIN(bounds.xmin, 0);
110 CLAMP_MAX(bounds.xmax, region->winx);
111 CLAMP_MIN(bounds.ymin, 0);
112 CLAMP_MAX(bounds.ymax, region->winy);
113 }
114 }
115
116 return section_bounds;
117}
118
120 const Span<rcti> section_bounds,
121 const ThemeColorID colorid,
122 const uiButtonSectionsAlign align,
123 const float corner_radius)
124{
125 float bg_color[4];
126 UI_GetThemeColor4fv(colorid, bg_color);
127
128 for (const rcti &bounds : section_bounds) {
129 int roundbox_corners = [align]() -> int {
130 switch (align) {
136 return UI_CNR_ALL;
137 }
138 return UI_CNR_ALL;
139 }();
140
141 /* No rounded corners at the region edge. */
142 if (bounds.xmin == 0) {
143 roundbox_corners &= ~(UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT);
144 }
145 if (bounds.xmax >= region->winx) {
146 roundbox_corners &= ~(UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT);
147 }
148
149 rctf bounds_float;
150 BLI_rctf_rcti_copy(&bounds_float, &bounds);
151 /* Make space for the separator line. */
152 if (align == uiButtonSectionsAlign::Top) {
154 }
155 else if (align == uiButtonSectionsAlign::Bottom) {
157 }
158
159 UI_draw_roundbox_corner_set(roundbox_corners);
160 UI_draw_roundbox_4fv(&bounds_float, true, corner_radius, bg_color);
161 }
162}
163
165 const Span<rcti> section_bounds,
166 const ThemeColorID colorid,
167 const uiButtonSectionsAlign align,
168 const float corner_radius)
169{
170 const int separator_line_width = UI_BUTTON_SECTION_SEPERATOR_LINE_WITH;
171
172 float bg_color[4];
173 UI_GetThemeColor4fv(colorid, bg_color);
174
176
177 /* Separator line. */
178 {
183 immUniformColor4fv(bg_color);
184
185 if (align == uiButtonSectionsAlign::Top) {
186 immRecti(pos, 0, region->winy - separator_line_width, region->winx, region->winy);
187 }
188 else if (align == uiButtonSectionsAlign::Bottom) {
189 immRecti(pos, 0, 0, region->winx, separator_line_width);
190 }
191 else {
193 }
195 }
196
197 int prev_xmax = 0;
198 for (const rcti &bounds : section_bounds) {
199 if (prev_xmax != 0) {
200 const rcti rounded_corner_rect = {
201 prev_xmax, bounds.xmin, separator_line_width, region->winy - separator_line_width};
202
206 ui_draw_rounded_corners_inverted(rounded_corner_rect, corner_radius, bg_color);
207 }
208
209 prev_xmax = bounds.xmax;
210 }
211
213}
214
216 const int /*ThemeColorID*/ colorid,
217 const uiButtonSectionsAlign align)
218{
219 const float aspect = BLI_rctf_size_x(&region->v2d.cur) /
220 (BLI_rcti_size_x(&region->v2d.mask) + 1);
221 const float corner_radius = 4.0f * UI_SCALE_FAC / aspect;
222
223 const Vector<rcti> section_bounds = button_section_bounds_calc(region, true);
224
226 region, section_bounds, ThemeColorID(colorid), align, corner_radius);
227 if (align != uiButtonSectionsAlign::None) {
229 section_bounds,
230 ThemeColorID(colorid),
231 align,
232 /* Slightly bigger corner radius, looks better. */
233 corner_radius + 1);
234 }
235}
236
237bool UI_region_button_sections_is_inside_x(const ARegion *region, const int mval_x)
238{
239 const Vector<rcti> section_bounds = button_section_bounds_calc(region, true);
240
241 for (const rcti &bounds : section_bounds) {
242 if (BLI_rcti_isect_x(&bounds, mval_x)) {
243 return true;
244 }
245 }
246 return false;
247}
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define LISTBASE_FOREACH(type, var, list)
void BLI_rcti_init_minmax(struct rcti *rect)
Definition rct.c:478
void BLI_rcti_pad(struct rcti *rect, int pad_x, int pad_y)
Definition rct.c:623
bool BLI_rcti_isect_x(const rcti *rect, int x)
Definition rct.c:37
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition BLI_rect.h:189
void BLI_rctf_rcti_copy(struct rctf *dst, const struct rcti *src)
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition BLI_rect.h:197
void BLI_rcti_do_minmax_rcti(struct rcti *rect, const struct rcti *other)
unsigned int uint
#define CLAMP_MAX(a, c)
#define CLAMP_MIN(a, b)
@ RGN_DRAWING
#define HEADER_PADDING_Y
#define UI_SCALE_FAC
void immUnbindProgram()
void immBindBuiltinProgram(eGPUBuiltinShader shader_id)
GPUVertFormat * immVertexFormat()
void immUniformColor4fv(const float rgba[4])
void immRecti(uint pos, int x1, int y1, int x2, int y2)
@ GPU_SHADER_3D_UNIFORM_COLOR
@ GPU_BLEND_NONE
Definition GPU_state.hh:85
@ GPU_BLEND_ALPHA
Definition GPU_state.hh:87
void GPU_blend(eGPUBlend blend)
Definition gpu_state.cc:42
@ GPU_FETCH_INT_TO_FLOAT
uint GPU_vertformat_attr_add(GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_I32
void UI_draw_roundbox_4fv(const rctf *rect, bool filled, float rad, const float col[4])
const uiStyle * UI_style_get_dpi()
void UI_draw_roundbox_corner_set(int type)
#define UI_BUTTON_SECTION_SEPERATOR_LINE_WITH
#define UI_BUTTON_SECTION_MERGE_DISTANCE
uiButtonSectionsAlign
@ UI_CNR_BOTTOM_LEFT
@ UI_CNR_BOTTOM_RIGHT
@ UI_CNR_ALL
@ UI_CNR_TOP_LEFT
@ UI_CNR_TOP_RIGHT
@ UI_BTYPE_SEPR_SPACER
ThemeColorID
void UI_GetThemeColor4fv(int colorid, float col[4])
void append(const T &value)
const T & last(const int64_t n=0) const
bool is_empty() const
void ui_but_to_pixelrect(rcti *rect, const ARegion *region, const uiBlock *block, const uiBut *but)
static void ui_draw_button_sections_background(const ARegion *region, const Span< rcti > section_bounds, const ThemeColorID colorid, const uiButtonSectionsAlign align, const float corner_radius)
void UI_region_button_sections_draw(const ARegion *region, const int colorid, const uiButtonSectionsAlign align)
bool UI_region_button_sections_is_inside_x(const ARegion *region, const int mval_x)
static void ui_draw_button_sections_alignment_separator(const ARegion *region, const Span< rcti > section_bounds, const ThemeColorID colorid, const uiButtonSectionsAlign align, const float corner_radius)
static Vector< rcti > button_section_bounds_calc(const ARegion *region, const bool add_padding)
void ui_draw_rounded_corners_inverted(const rcti &rect, const float rad, const blender::float4 color)
format
ccl_device_inline float3 ceil(const float3 a)
float ymax
float ymin
int xmin
int xmax