UI: Theme: Add Curve widget theme settings

Add theme settings for the Curve and CurveProfile widgets.

Even though the curve widget is used all over Blender, it never had its
own theme settings. It was using a mix of colors from the “regular”
toggle widget with text colors inherited from the editor.

Thanks to the recent cleanup and removal of so many redundant theme
options, we can now add new/missing settings without overwhelming
the theme.

See PR for details and screenshots.

Pull Request: https://projects.blender.org/blender/blender/pulls/146274
This commit is contained in:
Pablo Vazquez
2025-09-25 17:44:33 +02:00
committed by Pablo Vazquez
parent 79c1198770
commit b74e696acc
8 changed files with 123 additions and 106 deletions

View File

@@ -121,6 +121,16 @@ const bTheme U_theme_default = {
.text_sel = RGBA(0xffffffff),
.roundness = 0.2f,
},
.wcol_curve = {
.outline = RGBA(0x727272ff),
.outline_sel = RGBA(0x9a9a9aff),
.inner = RGBA(0x545454ff),
.inner_sel = RGBA(0x000000ff),
.item = RGBA(0x00000059),
.text = RGBA(0x08080800),
.text_sel = RGBA(0xc9c9c900),
.roundness = 0.2f,
},
.wcol_menu = {
.outline = RGBA(0x3d3d3dff),
.outline_sel = RGBA(0x3d3d3dff),

View File

@@ -1455,6 +1455,7 @@ class ThemeGenericClassGenerator:
def generate_panel_classes_for_wcols():
wcols = [
("Box", "wcol_box"),
("Curve", "wcol_curve"),
("List Item", "wcol_list_item"),
("Menu", "wcol_menu"),
("Menu Background", "wcol_menu_back"),

View File

@@ -27,7 +27,7 @@
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 92
#define BLENDER_FILE_SUBVERSION 93
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and cancel loading the file, showing a warning to

View File

@@ -375,6 +375,17 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme)
FROM_DEFAULT_V4_UCHAR(space_view3d.freestyle);
}
if (!USER_VERSION_ATLEAST(500, 93)) {
FROM_DEFAULT_V4_UCHAR(tui.wcol_curve.text);
FROM_DEFAULT_V4_UCHAR(tui.wcol_curve.text_sel);
FROM_DEFAULT_V4_UCHAR(tui.wcol_curve.item);
FROM_DEFAULT_V4_UCHAR(tui.wcol_curve.inner);
FROM_DEFAULT_V4_UCHAR(tui.wcol_curve.inner_sel);
FROM_DEFAULT_V4_UCHAR(tui.wcol_curve.outline);
FROM_DEFAULT_V4_UCHAR(tui.wcol_curve.outline_sel);
btheme->tui.wcol_curve.roundness = U_theme_default.tui.wcol_curve.roundness;
}
/**
* Always bump subversion in BKE_blender_version.h when adding versioning
* code here, and wrap it inside a USER_VERSION_ATLEAST check.

View File

@@ -1595,34 +1595,6 @@ static void ui_draw_but_curve_grid(const uint pos,
immEnd();
}
static void gl_shaded_color_get(const uchar color[3], int shade, uchar r_color[3])
{
r_color[0] = color[0] - shade > 0 ? color[0] - shade : 0;
r_color[1] = color[1] - shade > 0 ? color[1] - shade : 0;
r_color[2] = color[2] - shade > 0 ? color[2] - shade : 0;
}
static void gl_shaded_color_get_fl(const uchar *color, int shade, float r_color[3])
{
uchar color_shaded[3];
gl_shaded_color_get(color, shade, color_shaded);
rgb_uchar_to_float(r_color, color_shaded);
}
static void gl_shaded_color(const uchar *color, int shade)
{
uchar color_shaded[3];
gl_shaded_color_get(color, shade, color_shaded);
immUniformColor3ubv(color_shaded);
}
static void gl_shaded_color(const uchar *color, int shade, uchar alpha)
{
uchar color_shaded[3];
gl_shaded_color_get(color, shade, color_shaded);
immUniformColor3ubvAlpha(color_shaded, alpha);
}
void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol, const rcti *rect)
{
uiButCurveMapping *but_cumap = (uiButCurveMapping *)but;
@@ -1630,8 +1602,8 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol,
but_cumap->edit_cumap;
const bool inactive = but->flag & UI_BUT_INACTIVE;
const uchar alpha = inactive ? 192 : 255;
const float float_alpha = inactive ? 0.75f : 1.0f;
const float fade_factor_float = inactive ? 0.33f : 1.0f;
const uchar fade_factor_uchar = inactive ? 3 : 1;
const float clip_size_x = BLI_rctf_size_x(&cumap->curr);
const float clip_size_y = BLI_rctf_size_y(&cumap->curr);
@@ -1701,34 +1673,44 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol,
ui_draw_but_curve_grid(pos, rect, zoomx, zoomy, offsx, offsy, 0.1666666f);
}
else {
/* Draw backdrop. */
immUniformColor3ubvAlpha(wcol->inner, (wcol->inner[3] / fade_factor_uchar));
immRectf(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax);
/* Draw an outline around the clipped limits. */
if (cumap->flag & CUMA_DO_CLIP) {
gl_shaded_color_get_fl(wcol->inner, -20, color_backdrop);
immUniformColor3fvAlpha(color_backdrop, float_alpha);
immRectf(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax);
immUniformColor3ubvAlpha(wcol->inner, alpha);
immRectf(pos,
rect->xmin + zoomx * (cumap->clipr.xmin - offsx),
rect->ymin + zoomy * (cumap->clipr.ymin - offsy),
rect->xmin + zoomx * (cumap->clipr.xmax - offsx),
rect->ymin + zoomy * (cumap->clipr.ymax - offsy));
}
else {
rgb_uchar_to_float(color_backdrop, wcol->inner);
immUniformColor3fvAlpha(color_backdrop, float_alpha);
immRectf(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax);
format = immVertexFormat();
pos = GPU_vertformat_attr_add(format, "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
immUniformColor3ubvAlpha(wcol->item, (wcol->item[3] / fade_factor_uchar));
GPU_line_width(2.0f);
imm_draw_box_wire_2d(pos,
rect->xmin + zoomx * (cumap->clipr.xmin - offsx),
rect->ymin + zoomy * (cumap->clipr.ymin - offsy),
rect->xmin + zoomx * (cumap->clipr.xmax - offsx),
rect->ymin + zoomy * (cumap->clipr.ymax - offsy));
GPU_line_width(1.0f);
}
/* grid, every 0.25 step */
gl_shaded_color(wcol->inner, -16, alpha);
immUniformColor3ubvAlpha(wcol->outline_sel, 64 / fade_factor_uchar);
ui_draw_but_curve_grid(pos, rect, zoomx, zoomy, offsx, offsy, 0.25f);
/* grid, every 1.0 step */
gl_shaded_color(wcol->inner, -24, alpha);
immUniformColor3ubvAlpha(wcol->outline_sel, 92 / fade_factor_uchar);
ui_draw_but_curve_grid(pos, rect, zoomx, zoomy, offsx, offsy, 1.0f);
/* axes */
gl_shaded_color(wcol->inner, -50, alpha);
immBegin(GPU_PRIM_LINES, 4);
uchar col_axis_x[3], col_axis_y[3];
UI_GetThemeColor3ubv(TH_AXIS_X, col_axis_x);
UI_GetThemeColor3ubv(TH_AXIS_Y, col_axis_y);
immBegin(GPU_PRIM_LINES, 2);
immUniformColor3ubvAlpha(col_axis_x, 128 / fade_factor_uchar);
immVertex2f(pos, rect->xmin, rect->ymin + zoomy * (-offsy));
immVertex2f(pos, rect->xmax, rect->ymin + zoomy * (-offsy));
immEnd();
immBegin(GPU_PRIM_LINES, 2);
immUniformColor3ubvAlpha(col_axis_y, 128 / fade_factor_uchar);
immVertex2f(pos, rect->xmin + zoomx * (-offsx), rect->ymin);
immVertex2f(pos, rect->xmin + zoomx * (-offsx), rect->ymax);
immEnd();
@@ -1815,8 +1797,7 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol,
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* Curve filled. */
uchar filled_alpha = inactive ? 64 : 128;
immUniformColor3ubvAlpha(wcol->item, filled_alpha);
immUniformColor3ubvAlpha(wcol->item, wcol->item[3] / fade_factor_uchar);
immBegin(GPU_PRIM_TRI_STRIP, (CM_TABLE * 2 + 2) + 4);
immVertex2f(pos, line_range.xmin, rect->ymin);
immVertex2f(pos, line_range.xmin, line_range.ymin);
@@ -1832,7 +1813,7 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol,
/* Curve line. */
GPU_line_width(1.0f);
immUniformColor3ubvAlpha(wcol->item, alpha);
immUniformColor3ubvAlpha(wcol->inner_sel, wcol->inner_sel[3] / fade_factor_uchar);
GPU_line_smooth(true);
immBegin(GPU_PRIM_LINE_STRIP, (CM_TABLE + 1) + 2);
immVertex2f(pos, line_range.xmin, line_range.ymin);
@@ -1859,35 +1840,49 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol,
GPU_program_point_size(true);
/* Calculate vertex colors based on text theme. */
float color_vert[4], color_vert_select[4];
UI_GetThemeColor4fv(TH_TEXT_HI, color_vert);
UI_GetThemeColor4fv(TH_TEXT, color_vert_select);
if (inactive) {
color_vert[3] *= float_alpha;
color_vert_select[3] *= float_alpha;
}
if (len_squared_v3v3(color_vert, color_vert_select) < 0.1f) {
interp_v3_v3v3(color_vert, color_vert_select, color_backdrop, 0.75f);
}
if (len_squared_v3(color_vert) > len_squared_v3(color_vert_select)) {
/* Ensure brightest text color is used for selection. */
swap_v3_v3(color_vert, color_vert_select);
}
float color_point[4], color_point_select[4], color_point_outline[4];
rgba_uchar_to_float(color_point, wcol->text);
rgba_uchar_to_float(color_point_select, wcol->text_sel);
rgba_uchar_to_float(color_point_outline, wcol->inner_sel);
color_point[3] = fade_factor_float;
color_point_select[3] = fade_factor_float;
color_point_outline[3] *= fade_factor_float;
cmp = cuma->curve;
const float point_size = max_ff(U.pixelsize * 3.0f,
min_ff(UI_SCALE_FAC / but->block->aspect * 6.0f, 20.0f));
const float point_size = max_ff(U.pixelsize * 2.0f,
min_ff(UI_SCALE_FAC / but->block->aspect * 4.0f, 20.0f));
GPU_blend(GPU_BLEND_ALPHA);
/* Curve widgets using a gradient background (such as Hue Correct), draw
* an additional point in the back, forming an outline so they stand out. */
if (but_cumap->gradient_type == UI_GRAD_H) {
immBegin(GPU_PRIM_POINTS, cuma->totpoint);
for (int a = 0; a < cuma->totpoint; a++) {
const float fx = rect->xmin + zoomx * (cmp[a].x - offsx);
const float fy = rect->ymin + zoomy * (cmp[a].y - offsy);
const float size_factor = (cmp[a].flag & CUMA_SELECT) ? 1.4f : 1.2f;
/* First the point the back, slightly larger so it makes an outline. */
immAttr4fv(col, color_point_outline);
immAttr1f(size, point_size * size_factor);
immVertex2f(pos, fx, fy);
}
immEnd();
}
immBegin(GPU_PRIM_POINTS, cuma->totpoint);
for (int a = 0; a < cuma->totpoint; a++) {
const float fx = rect->xmin + zoomx * (cmp[a].x - offsx);
const float fy = rect->ymin + zoomy * (cmp[a].y - offsy);
immAttr4fv(col, (cmp[a].flag & CUMA_SELECT) ? color_vert_select : color_vert);
/* The point in front. */
immAttr4fv(col, (cmp[a].flag & CUMA_SELECT) ? color_point_select : color_point);
immAttr1f(size, point_size);
immVertex2f(pos, fx, fy);
}
immEnd();
immUnbindProgram();
GPU_blend(GPU_BLEND_NONE);
/* Restore scissor-test. */
GPU_scissor(scissor[0], scissor[1], scissor[2], scissor[3]);
@@ -1958,30 +1953,27 @@ void ui_draw_but_CURVEPROFILE(ARegion *region,
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* Draw the backdrop. */
float color_backdrop[4] = {0, 0, 0, 1};
if (profile->flag & PROF_USE_CLIP) {
gl_shaded_color_get_fl((uchar *)wcol->inner, -20, color_backdrop);
immUniformColor3fv(color_backdrop);
immRectf(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax);
immUniformColor3ubv((uchar *)wcol->inner);
GPU_blend(GPU_BLEND_ALPHA);
/* Main backdrop. */
immUniformColor4ubv(wcol->inner);
immRectf(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax);
if (!(profile->flag & PROF_USE_CLIP)) {
immUniformColor3ubvAlpha(wcol->outline_sel, 16);
immRectf(pos,
rect->xmin + zoomx * (profile->clip_rect.xmin - offsx),
rect->ymin + zoomy * (profile->clip_rect.ymin - offsy),
rect->xmin + zoomx * (profile->clip_rect.xmax - offsx),
rect->ymin + zoomy * (profile->clip_rect.ymax - offsy));
}
else {
rgb_uchar_to_float(color_backdrop, (uchar *)wcol->inner);
immUniformColor3fv(color_backdrop);
immRectf(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax);
}
/* 0.25 step grid. */
gl_shaded_color((uchar *)wcol->inner, -16);
immUniformColor3ubvAlpha(wcol->outline_sel, 64);
ui_draw_but_curve_grid(pos, rect, zoomx, zoomy, offsx, offsy, 0.25f);
/* 1.0 step grid. */
gl_shaded_color((uchar *)wcol->inner, -24);
immUniformColor3ubvAlpha(wcol->outline_sel, 92);
ui_draw_but_curve_grid(pos, rect, zoomx, zoomy, offsx, offsy, 1.0f);
GPU_blend(GPU_BLEND_NONE);
/* Draw the path's fill. */
if (profile->table == nullptr) {
@@ -2041,7 +2033,7 @@ void ui_draw_but_CURVEPROFILE(ARegion *region,
BLI_polyfill_calc(table_coords, tot_points, -1, tri_indices);
/* Draw the triangles for the profile fill. */
immUniformColor3ubvAlpha(wcol->item, 128);
immUniformColor4ubv(wcol->item);
GPU_blend(GPU_BLEND_ALPHA);
GPU_polygon_smooth(false);
immBegin(GPU_PRIM_TRIS, 3 * tot_triangles);
@@ -2062,7 +2054,7 @@ void ui_draw_but_CURVEPROFILE(ARegion *region,
const int edges_len = tot_points - 1;
if (edges_len > 0) {
GPU_line_width(1.0f);
immUniformColor3ubvAlpha((const uchar *)wcol->item, 255);
immUniformColor4ubv(wcol->inner_sel);
GPU_line_smooth(true);
immBegin(GPU_PRIM_LINE_STRIP, tot_points);
for (int i = 0; i < tot_points; i++) {
@@ -2087,7 +2079,7 @@ void ui_draw_but_CURVEPROFILE(ARegion *region,
/* Draw the lines to the handles from the points. */
if (selected_free_points > 0) {
GPU_line_width(1.0f);
gl_shaded_color((uchar *)wcol->inner, -24);
immUniformColor4ubv(wcol->inner_sel);
GPU_line_smooth(true);
immBegin(GPU_PRIM_LINES, selected_free_points * 4);
float ptx, pty;
@@ -2121,21 +2113,14 @@ void ui_draw_but_CURVEPROFILE(ARegion *region,
GPU_program_point_size(true);
/* Calculate vertex colors based on text theme. */
float color_vert[4], color_vert_select[4], color_sample[4];
UI_GetThemeColor4fv(TH_TEXT_HI, color_vert);
UI_GetThemeColor4fv(TH_TEXT, color_vert_select);
float color_point[4], color_point_select[4], color_point_outline[4], color_sample[4];
rgba_uchar_to_float(color_point, wcol->text);
rgba_uchar_to_float(color_point_select, wcol->text_sel);
rgba_uchar_to_float(color_point_outline, wcol->inner_sel);
color_sample[0] = float(wcol->item[0]) / 255.0f;
color_sample[1] = float(wcol->item[1]) / 255.0f;
color_sample[2] = float(wcol->item[2]) / 255.0f;
color_sample[3] = float(wcol->item[3]) / 255.0f;
if (len_squared_v3v3(color_vert, color_vert_select) < 0.1f) {
interp_v3_v3v3(color_vert, color_vert_select, color_backdrop, 0.75f);
}
if (len_squared_v3(color_vert) > len_squared_v3(color_vert_select)) {
/* Ensure brightest text color is used for selection. */
swap_v3_v3(color_vert, color_vert_select);
}
float point_size;
@@ -2143,13 +2128,17 @@ void ui_draw_but_CURVEPROFILE(ARegion *region,
GPU_line_smooth(false);
if (path_len > 0) {
GPU_blend(GPU_BLEND_NONE);
point_size = max_ff(3.0f, min_ff(UI_SCALE_FAC / but->block->aspect * 5.0f, 5.0f));
point_size = max_ff(U.pixelsize * 2.0f,
min_ff(UI_SCALE_FAC / but->block->aspect * 4.0f, 20.0f));
immBegin(GPU_PRIM_POINTS, path_len);
for (int i = 0; i < path_len; i++) {
fx = rect->xmin + zoomx * (pts[i].x - offsx);
fy = rect->ymin + zoomy * (pts[i].y - offsy);
immAttr1f(size, point_size);
immAttr4fv(col, (pts[i].flag & PROF_SELECT) ? color_vert_select : color_vert);
const float size_factor = (pts[i].flag & PROF_SELECT) ? 1.5f : 1.0f;
immAttr1f(size, point_size * size_factor);
immAttr4fv(col, (pts[i].flag & PROF_SELECT) ? color_point_select : color_point);
immVertex2f(pos, fx, fy);
}
immEnd();
@@ -2159,18 +2148,19 @@ void ui_draw_but_CURVEPROFILE(ARegion *region,
if (selected_free_points > 0) {
GPU_line_smooth(false);
GPU_blend(GPU_BLEND_NONE);
point_size = max_ff(2.0f, min_ff(UI_SCALE_FAC / but->block->aspect * 4.0f, 4.0f));
point_size = max_ff(U.pixelsize * 2.0f,
min_ff(UI_SCALE_FAC / but->block->aspect * 4.0f, 20.0f));
immBegin(GPU_PRIM_POINTS, selected_free_points * 2);
for (int i = 0; i < path_len; i++) {
if (point_draw_handles(&pts[i])) {
fx = rect->xmin + zoomx * (pts[i].h1_loc[0] - offsx);
fy = rect->ymin + zoomy * (pts[i].h1_loc[1] - offsy);
immAttr1f(size, point_size);
immAttr4fv(col, (pts[i].flag & PROF_H1_SELECT) ? color_vert_select : color_vert);
immAttr4fv(col, (pts[i].flag & PROF_H1_SELECT) ? color_point_select : color_point);
immVertex2f(pos, fx, fy);
fx = rect->xmin + zoomx * (pts[i].h2_loc[0] - offsx);
fy = rect->ymin + zoomy * (pts[i].h2_loc[1] - offsy);
immAttr4fv(col, (pts[i].flag & PROF_H2_SELECT) ? color_vert_select : color_vert);
immAttr4fv(col, (pts[i].flag & PROF_H2_SELECT) ? color_point_select : color_point);
immVertex2f(pos, fx, fy);
}
}

View File

@@ -5272,11 +5272,11 @@ void ui_draw_but(const bContext *C, ARegion *region, uiStyle *style, uiBut *but,
break;
case ButType::Curve:
ui_draw_but_CURVE(region, but, &tui->wcol_regular, rect);
ui_draw_but_CURVE(region, but, &tui->wcol_curve, rect);
break;
case ButType::CurveProfile:
ui_draw_but_CURVEPROFILE(region, but, &tui->wcol_regular, rect);
ui_draw_but_CURVEPROFILE(region, but, &tui->wcol_curve, rect);
break;
case ButType::Progress:

View File

@@ -211,7 +211,7 @@ typedef struct ThemeUI {
/* Interface Elements (buttons, menus, icons) */
uiWidgetColors wcol_regular, wcol_tool, wcol_toolbar_item, wcol_text;
uiWidgetColors wcol_radio, wcol_option, wcol_toggle;
uiWidgetColors wcol_num, wcol_numslider, wcol_tab;
uiWidgetColors wcol_num, wcol_numslider, wcol_tab, wcol_curve;
uiWidgetColors wcol_menu, wcol_pulldown, wcol_menu_back, wcol_menu_item, wcol_tooltip;
uiWidgetColors wcol_box, wcol_scroll, wcol_progress, wcol_list_item, wcol_pie_menu;

View File

@@ -1860,6 +1860,11 @@ static void rna_def_userdef_theme_ui(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Box Backdrop Colors", "");
RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
prop = RNA_def_property(srna, "wcol_curve", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_NEVER_NULL);
RNA_def_property_ui_text(prop, "Curve Widget Colors", "");
RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
prop = RNA_def_property(srna, "wcol_menu", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_NEVER_NULL);
RNA_def_property_ui_text(prop, "Menu Widget Colors", "");