The conversion from int to float is not supported natively so it ends up happening beforehand on the CPU or as a step before the vertex buffer can be used. It's better to just upload floats in the first place. Related to: -1e1ac2bb9b-617858e453Pull Request: https://projects.blender.org/blender/blender/pulls/138855
252 lines
8.5 KiB
C++
252 lines
8.5 KiB
C++
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup edinterface
|
|
*
|
|
* Calculating and drawing of bounding boxes for "button sections". That is, each group of buttons
|
|
* separated by a separator spacer button.
|
|
*/
|
|
|
|
#include "BLI_listbase.h"
|
|
#include "BLI_math_vector_types.hh"
|
|
#include "BLI_rect.h"
|
|
#include "BLI_span.hh"
|
|
#include "BLI_vector.hh"
|
|
|
|
#include "BKE_screen.hh"
|
|
|
|
#include "DNA_screen_types.h"
|
|
|
|
#include "GPU_immediate.hh"
|
|
#include "GPU_state.hh"
|
|
|
|
#include "interface_intern.hh"
|
|
|
|
using namespace blender;
|
|
|
|
/**
|
|
* Calculate a bounding box for each section. Sections will be merged if they are closer than
|
|
* #UI_BUTTON_SECTION_MERGE_DISTANCE.
|
|
*
|
|
* If a section is closer than #UI_BUTTON_SECTION_MERGE_DISTANCE to a region edge, it will be
|
|
* extended to the edge.
|
|
*
|
|
* \return the bounding boxes in region space.
|
|
*/
|
|
static Vector<rcti> button_section_bounds_calc(const ARegion *region, const bool add_padding)
|
|
{
|
|
Vector<rcti> section_bounds;
|
|
|
|
const auto finish_section_fn = [&](const rcti cur_section_bounds) {
|
|
if (!section_bounds.is_empty() &&
|
|
std::abs(section_bounds.last().xmax - cur_section_bounds.xmin) <
|
|
UI_BUTTON_SECTION_MERGE_DISTANCE)
|
|
{
|
|
section_bounds.last().xmax = cur_section_bounds.xmax;
|
|
}
|
|
else {
|
|
section_bounds.append(cur_section_bounds);
|
|
}
|
|
|
|
rcti &last_bounds = section_bounds.last();
|
|
/* Extend to region edge if close enough. */
|
|
if (last_bounds.xmin <= UI_BUTTON_SECTION_MERGE_DISTANCE) {
|
|
last_bounds.xmin = 0;
|
|
}
|
|
if (last_bounds.xmax >= (region->winx - UI_BUTTON_SECTION_MERGE_DISTANCE)) {
|
|
last_bounds.xmax = region->winx;
|
|
}
|
|
};
|
|
|
|
{
|
|
bool has_section_content = false;
|
|
rcti cur_section_bounds;
|
|
BLI_rcti_init_minmax(&cur_section_bounds);
|
|
|
|
/* A bit annoying, but this function is called for both drawing and event handling. When
|
|
* drawing, we need to exclude inactive blocks since they mess with the result. However, this
|
|
* active state is only useful during drawing and must be ignored for handling (at which point
|
|
* #uiBlock::active is false for all blocks). */
|
|
const bool is_drawing = region->runtime->do_draw & RGN_DRAWING;
|
|
LISTBASE_FOREACH (uiBlock *, block, ®ion->runtime->uiblocks) {
|
|
if (is_drawing && !block->active) {
|
|
continue;
|
|
}
|
|
|
|
for (const std::unique_ptr<uiBut> &but : block->buttons) {
|
|
if (but->type == UI_BTYPE_SEPR_SPACER) {
|
|
/* Start a new section. */
|
|
if (has_section_content) {
|
|
finish_section_fn(cur_section_bounds);
|
|
|
|
/* Reset for next section. */
|
|
BLI_rcti_init_minmax(&cur_section_bounds);
|
|
has_section_content = false;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
rcti but_pixelrect;
|
|
ui_but_to_pixelrect(&but_pixelrect, region, block, but.get());
|
|
BLI_rcti_do_minmax_rcti(&cur_section_bounds, &but_pixelrect);
|
|
has_section_content = true;
|
|
}
|
|
}
|
|
|
|
/* Finish last section in case the last button is not a spacer. */
|
|
if (has_section_content) {
|
|
finish_section_fn(cur_section_bounds);
|
|
}
|
|
}
|
|
|
|
if (add_padding) {
|
|
const uiStyle *style = UI_style_get_dpi();
|
|
const int pad_x = style->buttonspacex;
|
|
/* Making this based on the header size since this feature is typically used in headers, and
|
|
* this way we are more likely to pad the bounds all the way to the region edge. */
|
|
const int pad_y = ceil((HEADER_PADDING_Y * UI_SCALE_FAC) / 2.0f);
|
|
|
|
for (rcti &bounds : section_bounds) {
|
|
BLI_rcti_pad(&bounds, pad_x, pad_y);
|
|
/* Clamp, important for the rounded-corners to draw correct. */
|
|
CLAMP_MIN(bounds.xmin, 0);
|
|
CLAMP_MAX(bounds.xmax, region->winx);
|
|
CLAMP_MIN(bounds.ymin, 0);
|
|
CLAMP_MAX(bounds.ymax, region->winy);
|
|
}
|
|
}
|
|
|
|
return section_bounds;
|
|
}
|
|
|
|
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)
|
|
{
|
|
float bg_color[4];
|
|
UI_GetThemeColor4fv(colorid, bg_color);
|
|
|
|
for (const rcti &bounds : section_bounds) {
|
|
int roundbox_corners = [align]() -> int {
|
|
switch (align) {
|
|
case uiButtonSectionsAlign::Top:
|
|
return UI_CNR_BOTTOM_LEFT | UI_CNR_BOTTOM_RIGHT;
|
|
case uiButtonSectionsAlign::Bottom:
|
|
return UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT;
|
|
case uiButtonSectionsAlign::None:
|
|
return UI_CNR_ALL;
|
|
}
|
|
return UI_CNR_ALL;
|
|
}();
|
|
|
|
/* No rounded corners at the region edge. */
|
|
if (bounds.xmin == 0) {
|
|
roundbox_corners &= ~(UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT);
|
|
}
|
|
if (bounds.xmax >= region->winx) {
|
|
roundbox_corners &= ~(UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT);
|
|
}
|
|
|
|
rctf bounds_float;
|
|
BLI_rctf_rcti_copy(&bounds_float, &bounds);
|
|
/* Make space for the separator line. */
|
|
if (align == uiButtonSectionsAlign::Top) {
|
|
bounds_float.ymax -= UI_BUTTON_SECTION_SEPERATOR_LINE_WITH;
|
|
}
|
|
else if (align == uiButtonSectionsAlign::Bottom) {
|
|
bounds_float.ymin += UI_BUTTON_SECTION_SEPERATOR_LINE_WITH;
|
|
}
|
|
|
|
UI_draw_roundbox_corner_set(roundbox_corners);
|
|
UI_draw_roundbox_4fv(&bounds_float, true, corner_radius, bg_color);
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
const int separator_line_width = UI_BUTTON_SECTION_SEPERATOR_LINE_WITH;
|
|
|
|
float bg_color[4];
|
|
UI_GetThemeColor4fv(colorid, bg_color);
|
|
|
|
GPU_blend(GPU_BLEND_ALPHA);
|
|
|
|
/* Separator line. */
|
|
{
|
|
GPUVertFormat *format = immVertexFormat();
|
|
const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
|
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
|
|
immUniformColor4fv(bg_color);
|
|
|
|
if (align == uiButtonSectionsAlign::Top) {
|
|
immRectf(pos, 0, region->winy - separator_line_width, region->winx, region->winy);
|
|
}
|
|
else if (align == uiButtonSectionsAlign::Bottom) {
|
|
immRectf(pos, 0, 0, region->winx, separator_line_width);
|
|
}
|
|
else {
|
|
BLI_assert_unreachable();
|
|
}
|
|
immUnbindProgram();
|
|
}
|
|
|
|
int prev_xmax = 0;
|
|
for (const rcti &bounds : section_bounds) {
|
|
if (prev_xmax != 0) {
|
|
const rcti rounded_corner_rect = {
|
|
prev_xmax, bounds.xmin, separator_line_width, region->winy - separator_line_width};
|
|
|
|
UI_draw_roundbox_corner_set(align == uiButtonSectionsAlign::Top ?
|
|
(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT) :
|
|
(UI_CNR_BOTTOM_LEFT | UI_CNR_BOTTOM_RIGHT));
|
|
ui_draw_rounded_corners_inverted(rounded_corner_rect, corner_radius, bg_color);
|
|
}
|
|
|
|
prev_xmax = bounds.xmax;
|
|
}
|
|
|
|
GPU_blend(GPU_BLEND_NONE);
|
|
}
|
|
|
|
void UI_region_button_sections_draw(const ARegion *region,
|
|
const int /*ThemeColorID*/ colorid,
|
|
const uiButtonSectionsAlign align)
|
|
{
|
|
const float aspect = BLI_rctf_size_x(®ion->v2d.cur) /
|
|
(BLI_rcti_size_x(®ion->v2d.mask) + 1);
|
|
const float corner_radius = 4.0f * UI_SCALE_FAC / aspect;
|
|
|
|
const Vector<rcti> section_bounds = button_section_bounds_calc(region, true);
|
|
|
|
ui_draw_button_sections_background(
|
|
region, section_bounds, ThemeColorID(colorid), align, corner_radius);
|
|
if (align != uiButtonSectionsAlign::None) {
|
|
ui_draw_button_sections_alignment_separator(region,
|
|
section_bounds,
|
|
ThemeColorID(colorid),
|
|
align,
|
|
/* Slightly bigger corner radius, looks better. */
|
|
corner_radius + 1);
|
|
}
|
|
}
|
|
|
|
bool UI_region_button_sections_is_inside_x(const ARegion *region, const int mval_x)
|
|
{
|
|
const Vector<rcti> section_bounds = button_section_bounds_calc(region, true);
|
|
|
|
for (const rcti &bounds : section_bounds) {
|
|
if (BLI_rcti_isect_x(&bounds, mval_x)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|