From 23fb5752edc6fd148abac3106983dfaa1b4e6a63 Mon Sep 17 00:00:00 2001 From: Pablo Vazquez Date: Wed, 11 Jun 2025 23:20:50 +0200 Subject: [PATCH] UI: Theme: Add new Outline Selected property Allow theming the outline of selected elements. This helps to make active elements more prominent, and allows for flat theme combinations not possible before. Pull Request: https://projects.blender.org/blender/blender/pulls/139850 --- .../datafiles/userdef/userdef_default_theme.c | 20 +++++++++++ scripts/startup/bl_ui/space_userpref.py | 5 ++- .../blender/blenkernel/BKE_blender_version.h | 2 +- .../blenloader/intern/versioning_userdef.cc | 34 +++++++++++++++++++ .../editors/interface/interface_widgets.cc | 19 +++++++---- source/blender/makesdna/DNA_theme_types.h | 3 +- source/blender/makesrna/intern/rna_userdef.cc | 5 +++ 7 files changed, 79 insertions(+), 9 deletions(-) diff --git a/release/datafiles/userdef/userdef_default_theme.c b/release/datafiles/userdef/userdef_default_theme.c index 0e00287e25c..38a082b7e54 100644 --- a/release/datafiles/userdef/userdef_default_theme.c +++ b/release/datafiles/userdef/userdef_default_theme.c @@ -27,6 +27,7 @@ const bTheme U_theme_default = { .tui = { .wcol_regular = { .outline = RGBA(0x3d3d3dff), + .outline_sel = RGBA(0x3d3d3dff), .inner = RGBA(0x545454ff), .inner_sel = RGBA(0x4772b3ff), .item = RGBA(0x1d1d1d80), @@ -36,6 +37,7 @@ const bTheme U_theme_default = { }, .wcol_tool = { .outline = RGBA(0x3d3d3dff), + .outline_sel = RGBA(0x3d3d3dff), .inner = RGBA(0x545454ff), .inner_sel = RGBA(0x4772b3ff), .item = RGBA(0xffffffff), @@ -45,6 +47,7 @@ const bTheme U_theme_default = { }, .wcol_toolbar_item = { .outline = RGBA(0x3d3d3dff), + .outline_sel = RGBA(0x3d3d3dff), .inner = RGBA(0x282828ff), .inner_sel = RGBA(0x4772b3ff), .item = RGBA(0xffffffb3), @@ -54,6 +57,7 @@ const bTheme U_theme_default = { }, .wcol_text = { .outline = RGBA(0x3d3d3dff), + .outline_sel = RGBA(0x3d3d3dff), .inner = RGBA(0x1d1d1dff), .inner_sel = RGBA(0x181818ff), .item = RGBA(0xffffff33), @@ -63,6 +67,7 @@ const bTheme U_theme_default = { }, .wcol_radio = { .outline = RGBA(0x3d3d3dff), + .outline_sel = RGBA(0x3d3d3dff), .inner = RGBA(0x545454ff), .inner_sel = RGBA(0x4772b3ff), .item = RGBA(0x252525ff), @@ -72,6 +77,7 @@ const bTheme U_theme_default = { }, .wcol_option = { .outline = RGBA(0x3d3d3dff), + .outline_sel = RGBA(0x3d3d3dff), .inner = RGBA(0x545454ff), .inner_sel = RGBA(0x4772b3ff), .item = RGBA(0xffffffff), @@ -81,6 +87,7 @@ const bTheme U_theme_default = { }, .wcol_toggle = { .outline = RGBA(0x3d3d3dff), + .outline_sel = RGBA(0x3d3d3dff), .inner = RGBA(0x545454ff), .inner_sel = RGBA(0x4772b3ff), .item = RGBA(0x252525ff), @@ -90,6 +97,7 @@ const bTheme U_theme_default = { }, .wcol_num = { .outline = RGBA(0x3d3d3dff), + .outline_sel = RGBA(0x3d3d3dff), .inner = RGBA(0x545454ff), .inner_sel = RGBA(0x222222ff), .item = RGBA(0x4772b3ff), @@ -99,6 +107,7 @@ const bTheme U_theme_default = { }, .wcol_numslider = { .outline = RGBA(0x3d3d3dff), + .outline_sel = RGBA(0x3d3d3dff), .inner = RGBA(0x545454ff), .inner_sel = RGBA(0x222222ff), .item = RGBA(0x4772b3ff), @@ -108,6 +117,7 @@ const bTheme U_theme_default = { }, .wcol_tab = { .outline = RGBA(0x1d1d1dff), + .outline_sel = RGBA(0x1d1d1dff), .inner = RGBA(0x1d1d1dff), .inner_sel = RGBA(0x303030ff), .item = RGBA(0x1d1d1dff), @@ -117,6 +127,7 @@ const bTheme U_theme_default = { }, .wcol_menu = { .outline = RGBA(0x3d3d3dff), + .outline_sel = RGBA(0x3d3d3dff), .inner = RGBA(0x282828ff), .inner_sel = RGBA(0x4772b3b3), .item = RGBA(0xd9d9d9ff), @@ -126,6 +137,7 @@ const bTheme U_theme_default = { }, .wcol_pulldown = { .outline = RGBA(0x3d3d3d00), + .outline_sel = RGBA(0x3d3d3d00), .inner = RGBA(0x22222200), .inner_sel = RGBA(0xffffff1a), .item = RGBA(0xffffff8f), @@ -135,6 +147,7 @@ const bTheme U_theme_default = { }, .wcol_menu_back = { .outline = RGBA(0x242424ff), + .outline_sel = RGBA(0x242424ff), .inner = RGBA(0x181818ff), .inner_sel = RGBA(0x4772b3ff), .item = RGBA(0xd9d9d9ff), @@ -144,6 +157,7 @@ const bTheme U_theme_default = { }, .wcol_menu_item = { .outline = RGBA(0x3d3d3d00), + .outline_sel = RGBA(0x3d3d3d00), .inner = RGBA(0x18181800), .inner_sel = RGBA(0x4772b3ff), .item = RGBA(0xffffff8f), @@ -153,6 +167,7 @@ const bTheme U_theme_default = { }, .wcol_tooltip = { .outline = RGBA(0x242424ff), + .outline_sel = RGBA(0x242424ff), .inner = RGBA(0x1d1d1dff), .inner_sel = RGBA(0x4772b3ff), .item = RGBA(0xd9d9d9ff), @@ -162,6 +177,7 @@ const bTheme U_theme_default = { }, .wcol_box = { .outline = RGBA(0x3d3d3dff), + .outline_sel = RGBA(0x3d3d3dff), .inner = RGBA(0x1d1d1d80), .inner_sel = RGBA(0x545454ff), .item = RGBA(0x191919ff), @@ -171,6 +187,7 @@ const bTheme U_theme_default = { }, .wcol_scroll = { .outline = RGBA(0x3d3d3dff), + .outline_sel = RGBA(0x3d3d3dff), .inner = RGBA(0x22222200), .inner_sel = RGBA(0xffffffff), .item = RGBA(0x545454ff), @@ -180,6 +197,7 @@ const bTheme U_theme_default = { }, .wcol_progress = { .outline = RGBA(0x3d3d3dff), + .outline_sel = RGBA(0x3d3d3dff), .inner = RGBA(0x222222ff), .inner_sel = RGBA(0x4772b3ff), .item = RGBA(0x4772b3ff), @@ -189,6 +207,7 @@ const bTheme U_theme_default = { }, .wcol_list_item = { .outline = RGBA(0x2d2d2dff), + .outline_sel = RGBA(0x2d2d2dff), .inner = RGBA(0xffffff00), .inner_sel = RGBA(0x4772b3ff), .item = RGBA(0xffffff33), @@ -198,6 +217,7 @@ const bTheme U_theme_default = { }, .wcol_pie_menu = { .outline = RGBA(0x242424ff), + .outline_sel = RGBA(0x242424ff), .inner = RGBA(0x181818ff), .inner_sel = RGBA(0x4772b3ff), .item = RGBA(0x545454ff), diff --git a/scripts/startup/bl_ui/space_userpref.py b/scripts/startup/bl_ui/space_userpref.py index 71e2e251a28..568268dc2ce 100644 --- a/scripts/startup/bl_ui/space_userpref.py +++ b/scripts/startup/bl_ui/space_userpref.py @@ -1061,7 +1061,7 @@ class PreferenceThemeWidgetColorPanel: layout.use_property_split = True - flow = layout.grid_flow(row_major=False, columns=2, even_columns=True, even_rows=False, align=False) + flow = layout.grid_flow(row_major=True, columns=3, even_columns=True, even_rows=False, align=False) col = flow.column(align=True) col.prop(widget_style, "text") @@ -1071,7 +1071,10 @@ class PreferenceThemeWidgetColorPanel: col = flow.column(align=True) col.prop(widget_style, "inner", slider=True) col.prop(widget_style, "inner_sel", text="Selected", slider=True) + + col = flow.column(align=True) col.prop(widget_style, "outline") + col.prop(widget_style, "outline_sel", text="Selected", slider=True) col.separator() diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 80629ed9753..05905726365 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -27,7 +27,7 @@ /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 5 +#define BLENDER_FILE_SUBVERSION 6 /* 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 diff --git a/source/blender/blenloader/intern/versioning_userdef.cc b/source/blender/blenloader/intern/versioning_userdef.cc index 066002f0ad5..5f773c3cfb6 100644 --- a/source/blender/blenloader/intern/versioning_userdef.cc +++ b/source/blender/blenloader/intern/versioning_userdef.cc @@ -260,6 +260,40 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme) FROM_DEFAULT_V4_UCHAR(space_spreadsheet.tab_back); } + if (!USER_VERSION_ATLEAST(500, 6)) { + /* Match the selected/unselected outline colors. */ + copy_v4_v4_uchar(btheme->tui.wcol_box.outline_sel, U_theme_default.tui.wcol_box.outline); + copy_v4_v4_uchar(btheme->tui.wcol_list_item.outline_sel, + U_theme_default.tui.wcol_list_item.outline); + copy_v4_v4_uchar(btheme->tui.wcol_menu.outline_sel, U_theme_default.tui.wcol_menu.outline); + copy_v4_v4_uchar(btheme->tui.wcol_menu_back.outline_sel, + U_theme_default.tui.wcol_menu_back.outline); + copy_v4_v4_uchar(btheme->tui.wcol_menu_item.outline_sel, + U_theme_default.tui.wcol_menu_item.outline); + copy_v4_v4_uchar(btheme->tui.wcol_num.outline_sel, U_theme_default.tui.wcol_num.outline); + copy_v4_v4_uchar(btheme->tui.wcol_numslider.outline_sel, + U_theme_default.tui.wcol_numslider.outline); + copy_v4_v4_uchar(btheme->tui.wcol_option.outline_sel, U_theme_default.tui.wcol_option.outline); + copy_v4_v4_uchar(btheme->tui.wcol_pie_menu.outline_sel, + U_theme_default.tui.wcol_pie_menu.outline); + copy_v4_v4_uchar(btheme->tui.wcol_progress.outline_sel, + U_theme_default.tui.wcol_progress.outline); + copy_v4_v4_uchar(btheme->tui.wcol_pulldown.outline_sel, + U_theme_default.tui.wcol_pulldown.outline); + copy_v4_v4_uchar(btheme->tui.wcol_radio.outline_sel, U_theme_default.tui.wcol_radio.outline); + copy_v4_v4_uchar(btheme->tui.wcol_regular.outline_sel, + U_theme_default.tui.wcol_regular.outline); + copy_v4_v4_uchar(btheme->tui.wcol_scroll.outline_sel, U_theme_default.tui.wcol_scroll.outline); + copy_v4_v4_uchar(btheme->tui.wcol_tab.outline_sel, U_theme_default.tui.wcol_tab.outline); + copy_v4_v4_uchar(btheme->tui.wcol_text.outline_sel, U_theme_default.tui.wcol_text.outline); + copy_v4_v4_uchar(btheme->tui.wcol_toggle.outline_sel, U_theme_default.tui.wcol_toggle.outline); + copy_v4_v4_uchar(btheme->tui.wcol_tool.outline_sel, U_theme_default.tui.wcol_tool.outline); + copy_v4_v4_uchar(btheme->tui.wcol_toolbar_item.outline_sel, + U_theme_default.tui.wcol_toolbar_item.outline); + copy_v4_v4_uchar(btheme->tui.wcol_tooltip.outline_sel, + U_theme_default.tui.wcol_tooltip.outline); + } + /** * Always bump subversion in BKE_blender_version.h when adding versioning * code here, and wrap it inside a USER_VERSION_ATLEAST check. diff --git a/source/blender/editors/interface/interface_widgets.cc b/source/blender/editors/interface/interface_widgets.cc index ccaead6f0aa..34b534bdc5e 100644 --- a/source/blender/editors/interface/interface_widgets.cc +++ b/source/blender/editors/interface/interface_widgets.cc @@ -2553,6 +2553,7 @@ static void ui_widget_color_disabled(uiWidgetType *wt, const uiWidgetStateInfo * const float factor = widget_alpha_factor(state); wcol_theme_s.outline[3] *= factor; + wcol_theme_s.outline_sel[3] *= factor; wcol_theme_s.inner[3] *= factor; wcol_theme_s.inner_sel[3] *= factor; wcol_theme_s.item[3] *= factor; @@ -2566,7 +2567,8 @@ static void widget_active_color(uiWidgetColors *wcol) { const bool dark = (srgb_to_grayscale_byte(wcol->text) > srgb_to_grayscale_byte(wcol->inner)); color_mul_hsl_v3(wcol->inner, 1.0f, 1.15f, dark ? 1.2f : 1.1f); - color_mul_hsl_v3(wcol->outline, 1.0f, 1.15f, 1.15f); + color_blend_v4_v4v4(wcol->outline, wcol->outline, wcol->outline_sel, 0.5f); + color_mul_hsl_v3(wcol->outline_sel, 1.0f, 1.15f, 1.15f); color_mul_hsl_v3(wcol->text, 1.0f, 1.15f, dark ? 1.25f : 0.8f); } @@ -2621,6 +2623,7 @@ static void widget_state(uiWidgetType *wt, if (state->but_flag & UI_SELECT) { copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel); + copy_v4_v4_uchar(wt->wcol.outline, wt->wcol.outline_sel); if (color_blend != nullptr) { color_blend_v3_v3(wt->wcol.inner, color_blend, wcol_state->blend); } @@ -2632,6 +2635,7 @@ static void widget_state(uiWidgetType *wt, else { if (state->but_flag & UI_BUT_ACTIVE_DEFAULT) { copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel); + copy_v4_v4_uchar(wt->wcol.outline, wt->wcol.outline_sel); copy_v4_v4_uchar(wt->wcol.text, wt->wcol.text_sel); } if (color_blend != nullptr) { @@ -2662,6 +2666,7 @@ static void widget_state(uiWidgetType *wt, if (state->but_flag & UI_BUT_DRAG_MULTI) { /* the button isn't SELECT but we're editing this so draw with sel color */ copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel); + copy_v4_v4_uchar(wt->wcol.outline, wt->wcol.outline_sel); std::swap(wt->wcol.shadetop, wt->wcol.shadedown); color_blend_v3_v3(wt->wcol.text, wt->wcol.text_sel, 0.85f); } @@ -2794,6 +2799,7 @@ static void widget_state_pie_menu_item(uiWidgetType *wt, if ((state->but_flag & UI_BUT_DISABLED) && (state->but_flag & UI_HOVER)) { color_blend_v3_v3(wt->wcol.text, wt->wcol.text_sel, 0.5f); + color_blend_v3_v3(wt->wcol.outline, wt->wcol.outline_sel, 0.5f); /* draw the backdrop at low alpha, helps navigating with keys * when disabled items are active */ copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.item); @@ -2803,6 +2809,7 @@ static void widget_state_pie_menu_item(uiWidgetType *wt, /* regular active */ if (state->but_flag & (UI_SELECT | UI_HOVER)) { copy_v3_v3_uchar(wt->wcol.text, wt->wcol.text_sel); + copy_v3_v3_uchar(wt->wcol.outline, wt->wcol.outline_sel); } else if (state->but_flag & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) { /* regular disabled */ @@ -2811,9 +2818,11 @@ static void widget_state_pie_menu_item(uiWidgetType *wt, if (state->but_flag & UI_SELECT) { copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel); + copy_v4_v4_uchar(wt->wcol.outline, wt->wcol.outline_sel); } else if (state->but_flag & UI_HOVER) { copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.item); + color_blend_v3_v3(wt->wcol.outline, wt->wcol.outline_sel, 0.5f); } } } @@ -2847,6 +2856,7 @@ static void widget_state_menu_item(uiWidgetType *wt, else if (state->but_flag & (UI_BUT_ACTIVE_DEFAULT | UI_SELECT_DRAW)) { /* Currently-selected item. */ copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel); + copy_v4_v4_uchar(wt->wcol.outline, wt->wcol.outline_sel); copy_v4_v4_uchar(wt->wcol.text, wt->wcol.text_sel); } else if ((state->but_flag & (UI_SELECT | UI_BUT_ICON_PREVIEW)) == @@ -2854,11 +2864,13 @@ static void widget_state_menu_item(uiWidgetType *wt, { /* Currently-selected list or menu item that is large icon preview. */ copy_v4_v4_uchar(wt->wcol.inner, wt->wcol.inner_sel); + copy_v4_v4_uchar(wt->wcol.outline, wt->wcol.outline_sel); copy_v4_v4_uchar(wt->wcol.text, wt->wcol.text_sel); } else if (state->but_flag & UI_HOVER) { /* Regular hover. */ color_blend_v3_v3(wt->wcol.inner, wt->wcol.text, 0.2f); + color_blend_v3_v3(wt->wcol.outline, wt->wcol.outline_sel, 0.5f); copy_v3_v3_uchar(wt->wcol.text, wt->wcol.text_sel); wt->wcol.inner[3] = 255; wt->wcol.text[3] = 255; @@ -4286,9 +4298,6 @@ static void widget_menu_itembut(uiWidgetColors *wcol, rect->xmin += padding; rect->xmax -= padding; - /* No outline. */ - wtb.draw_outline = false; - const float rad = widget_radius_from_zoom(zoom, wcol); round_box_edges(&wtb, UI_CNR_ALL, rect, rad); @@ -4366,8 +4375,6 @@ static void widget_list_itembut(uiBut *but, uiWidgetBase wtb; widget_init(&wtb); - /* no outline */ - wtb.draw_outline = false; const float rad = widget_radius_from_zoom(zoom, wcol); round_box_edges(&wtb, UI_CNR_ALL, &draw_rect, rad); diff --git a/source/blender/makesdna/DNA_theme_types.h b/source/blender/makesdna/DNA_theme_types.h index c600b5c4957..7daba45f30d 100644 --- a/source/blender/makesdna/DNA_theme_types.h +++ b/source/blender/makesdna/DNA_theme_types.h @@ -108,13 +108,14 @@ typedef struct uiStyle { typedef struct uiWidgetColors { unsigned char outline[4]; + unsigned char outline_sel[4]; unsigned char inner[4]; unsigned char inner_sel[4]; unsigned char item[4]; unsigned char text[4]; unsigned char text_sel[4]; unsigned char shaded; - char _pad0[7]; + char _pad0[3]; short shadetop, shadedown; float roundness; } uiWidgetColors; diff --git a/source/blender/makesrna/intern/rna_userdef.cc b/source/blender/makesrna/intern/rna_userdef.cc index 23ca95d2156..6fd481f9ace 100644 --- a/source/blender/makesrna/intern/rna_userdef.cc +++ b/source/blender/makesrna/intern/rna_userdef.cc @@ -1606,6 +1606,11 @@ static void rna_def_userdef_theme_ui_wcol(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Outline", ""); RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); + prop = RNA_def_property(srna, "outline_sel", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_array(prop, 4); + RNA_def_property_ui_text(prop, "Outline Selected", ""); + RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); + prop = RNA_def_property(srna, "inner", PROP_FLOAT, PROP_COLOR_GAMMA); RNA_def_property_array(prop, 4); RNA_def_property_ui_text(prop, "Inner", "");