Blender V5.0
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
11
12#include "BLI_listbase.h"
14#include "BLI_rect.h"
15#include "BLI_span.hh"
16#include "BLI_vector.hh"
17
18#include "BKE_screen.hh"
19
20#include "DNA_screen_types.h"
21
22#include "GPU_immediate.hh"
23#include "GPU_state.hh"
24
25#include "interface_intern.hh"
26
27using namespace blender;
28
38static Vector<rcti> button_section_bounds_calc(const ARegion *region, const bool add_padding)
39{
40 Vector<rcti> section_bounds;
41
42 const auto finish_section_fn = [&](const rcti cur_section_bounds) {
43 if (!section_bounds.is_empty() &&
44 std::abs(section_bounds.last().xmax - cur_section_bounds.xmin) <
46 {
47 section_bounds.last().xmax = cur_section_bounds.xmax;
48 }
49 else {
50 section_bounds.append(cur_section_bounds);
51 }
52
53 rcti &last_bounds = section_bounds.last();
54 /* Extend to region edge if close enough. */
55 if (last_bounds.xmin <= UI_BUTTON_SECTION_MERGE_DISTANCE) {
56 last_bounds.xmin = 0;
57 }
58 if (last_bounds.xmax >= (region->winx - UI_BUTTON_SECTION_MERGE_DISTANCE)) {
59 last_bounds.xmax = region->winx;
60 }
61 };
62
63 {
64 bool has_section_content = false;
65 rcti cur_section_bounds;
66 BLI_rcti_init_minmax(&cur_section_bounds);
67
68 /* A bit annoying, but this function is called for both drawing and event handling. When
69 * drawing, we need to exclude inactive blocks since they mess with the result. However, this
70 * active state is only useful during drawing and must be ignored for handling (at which point
71 * #uiBlock::active is false for all blocks). */
72 const bool is_drawing = region->runtime->do_draw & RGN_DRAWING;
73 LISTBASE_FOREACH (uiBlock *, block, &region->runtime->uiblocks) {
74 if (is_drawing && !block->active) {
75 continue;
76 }
77
78 for (const std::unique_ptr<uiBut> &but : block->buttons) {
79 if (but->type == ButType::SeprSpacer) {
80 /* Start a new section. */
81 if (has_section_content) {
82 finish_section_fn(cur_section_bounds);
83
84 /* Reset for next section. */
85 BLI_rcti_init_minmax(&cur_section_bounds);
86 has_section_content = false;
87 }
88 continue;
89 }
90
91 rcti but_pixelrect;
92 ui_but_to_pixelrect(&but_pixelrect, region, block, but.get());
93 BLI_rcti_do_minmax_rcti(&cur_section_bounds, &but_pixelrect);
94 has_section_content = true;
95 }
96 }
97
98 /* Finish last section in case the last button is not a spacer. */
99 if (has_section_content) {
100 finish_section_fn(cur_section_bounds);
101 }
102 }
103
104 if (add_padding) {
105 const uiStyle *style = UI_style_get_dpi();
106 const int pad_x = style->buttonspacex;
107 /* Making this based on the header size since this feature is typically used in headers, and
108 * this way we are more likely to pad the bounds all the way to the region edge. */
109 const int pad_y = ceil((HEADER_PADDING_Y * UI_SCALE_FAC) / 2.0f);
110
111 for (rcti &bounds : section_bounds) {
112 BLI_rcti_pad(&bounds, pad_x, pad_y);
113 /* Clamp, important for the rounded-corners to draw correct. */
114 CLAMP_MIN(bounds.xmin, 0);
115 CLAMP_MAX(bounds.xmax, region->winx);
116 CLAMP_MIN(bounds.ymin, 0);
117 CLAMP_MAX(bounds.ymax, region->winy);
118 }
119 }
120
121 return section_bounds;
122}
123
125 const Span<rcti> section_bounds,
126 const ThemeColorID colorid,
127 const uiButtonSectionsAlign align,
128 const float corner_radius)
129{
130 float bg_color[4];
131 UI_GetThemeColor4fv(colorid, bg_color);
132
133 for (const rcti &bounds : section_bounds) {
134 int roundbox_corners = [align]() -> int {
135 switch (align) {
141 return UI_CNR_ALL;
142 }
143 return UI_CNR_ALL;
144 }();
145
146 /* No rounded corners at the region edge. */
147 if (bounds.xmin == 0) {
148 roundbox_corners &= ~(UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT);
149 }
150 if (bounds.xmax >= region->winx) {
151 roundbox_corners &= ~(UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT);
152 }
153
154 rctf bounds_float;
155 BLI_rctf_rcti_copy(&bounds_float, &bounds);
156 /* Make space for the separator line. */
157 if (align == uiButtonSectionsAlign::Top) {
159 }
160 else if (align == uiButtonSectionsAlign::Bottom) {
162 }
163
164 UI_draw_roundbox_corner_set(roundbox_corners);
165 UI_draw_roundbox_4fv(&bounds_float, true, corner_radius, bg_color);
166 }
167}
168
170 const Span<rcti> section_bounds,
171 const ThemeColorID colorid,
172 const uiButtonSectionsAlign align,
173 const float corner_radius)
174{
175 const int separator_line_width = UI_BUTTON_SECTION_SEPERATOR_LINE_WITH;
176
177 float bg_color[4];
178 UI_GetThemeColor4fv(colorid, bg_color);
179
181
182 /* Separator line. */
183 {
186 format, "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
188 immUniformColor4fv(bg_color);
189
190 if (align == uiButtonSectionsAlign::Top) {
191 immRectf(pos, 0, region->winy - separator_line_width, region->winx, region->winy);
192 }
193 else if (align == uiButtonSectionsAlign::Bottom) {
194 immRectf(pos, 0, 0, region->winx, separator_line_width);
195 }
196 else {
198 }
200 }
201
202 int prev_xmax = 0;
203 for (const rcti &bounds : section_bounds) {
204 if (prev_xmax != 0) {
205 const rcti rounded_corner_rect = {
206 prev_xmax, bounds.xmin, separator_line_width, region->winy - separator_line_width};
207
211 ui_draw_rounded_corners_inverted(rounded_corner_rect, corner_radius, bg_color);
212 }
213
214 prev_xmax = bounds.xmax;
215 }
216
218}
219
221 const int /*ThemeColorID*/ colorid,
222 const uiButtonSectionsAlign align)
223{
224 const float aspect = BLI_rctf_size_x(&region->v2d.cur) /
225 (BLI_rcti_size_x(&region->v2d.mask) + 1);
226 const float corner_radius = 4.0f * UI_SCALE_FAC / aspect;
227
228 const Vector<rcti> section_bounds = button_section_bounds_calc(region, true);
229
231 region, section_bounds, ThemeColorID(colorid), align, corner_radius);
232 if (align != uiButtonSectionsAlign::None) {
234 section_bounds,
235 ThemeColorID(colorid),
236 align,
237 /* Slightly bigger corner radius, looks better. */
238 corner_radius + 1);
239 }
240}
241
242bool UI_region_button_sections_is_inside_x(const ARegion *region, const int mval_x)
243{
244 const Vector<rcti> section_bounds = button_section_bounds_calc(region, true);
245
246 for (const rcti &bounds : section_bounds) {
247 if (BLI_rcti_isect_x(&bounds, mval_x)) {
248 return true;
249 }
250 }
251 return false;
252}
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define LISTBASE_FOREACH(type, var, list)
void BLI_rcti_init_minmax(struct rcti *rect)
Definition rct.cc:474
void BLI_rcti_pad(struct rcti *rect, int pad_x, int pad_y)
Definition rct.cc:629
bool BLI_rcti_isect_x(const rcti *rect, int x)
Definition rct.cc:37
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition BLI_rect.h:194
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:202
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(GPUBuiltinShader shader_id)
GPUVertFormat * immVertexFormat()
void immUniformColor4fv(const float rgba[4])
void immRectf(uint pos, float x1, float y1, float x2, float 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(GPUBlend blend)
Definition gpu_state.cc:42
uint GPU_vertformat_attr_add(GPUVertFormat *format, blender::StringRef name, blender::gpu::VertAttrType type)
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)
@ UI_CNR_BOTTOM_LEFT
@ UI_CNR_BOTTOM_RIGHT
@ UI_CNR_ALL
@ UI_CNR_TOP_LEFT
@ UI_CNR_TOP_RIGHT
#define UI_BUTTON_SECTION_SEPERATOR_LINE_WITH
#define UI_BUTTON_SECTION_MERGE_DISTANCE
uiButtonSectionsAlign
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
uint pos
#define ceil
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
ARegionRuntimeHandle * runtime
float ymax
float ymin
int xmin
int xmax
short buttonspacex