diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 3c3f7628ade..8022d54a01f 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -86,6 +86,11 @@ typedef struct uiViewItemHandle uiViewItemHandle; #define UI_SEP_CHAR '|' #define UI_SEP_CHAR_S "|" +/** + * Character used when value is indeterminate (multiple, unknown, unset). + */ +#define UI_VALUE_INDETERMINATE_CHAR BLI_STR_UTF8_EM_DASH + /* Separator for text in search menus (right pointing arrow). * keep in sync with `string_search.cc`. */ #define UI_MENU_ARROW_SEP BLI_STR_UTF8_BLACK_RIGHT_POINTING_SMALL_TRIANGLE @@ -326,6 +331,9 @@ enum { /* Draw the checkbox buttons inverted. */ UI_BUT_CHECKBOX_INVERT = 1 << 25, + + /* Drawn in a way that indicates that the state/value is unknown. */ + UI_BUT_INDETERMINATE = 1 << 26, }; /** diff --git a/source/blender/editors/interface/interface_intern.hh b/source/blender/editors/interface/interface_intern.hh index 2532dcb3bb7..9ade71fb8a4 100644 --- a/source/blender/editors/interface/interface_intern.hh +++ b/source/blender/editors/interface/interface_intern.hh @@ -1127,6 +1127,7 @@ enum { ROUNDBOX_TRIA_MENU, ROUNDBOX_TRIA_CHECK, ROUNDBOX_TRIA_HOLD_ACTION_ARROW, + ROUNDBOX_TRIA_DASH, ROUNDBOX_TRIA_MAX, /* don't use */ }; diff --git a/source/blender/editors/interface/interface_widgets.cc b/source/blender/editors/interface/interface_widgets.cc index 9807269e25f..2018a9459f3 100644 --- a/source/blender/editors/interface/interface_widgets.cc +++ b/source/blender/editors/interface/interface_widgets.cc @@ -1032,6 +1032,16 @@ static void shape_preset_trias_from_rect_checkmark(uiWidgetTrias *tria, const rc tria->index = g_shape_preset_checkmark_face; } +static void shape_preset_trias_from_rect_dash(uiWidgetTrias *tria, const rcti *rect) +{ + tria->type = ROUNDBOX_TRIA_DASH; + + /* Center position and size. */ + tria->center[0] = rect->xmin + 0.5f * BLI_rcti_size_y(rect); + tria->center[1] = rect->ymin + 0.5f * BLI_rcti_size_y(rect); + tria->size = 0.5f * BLI_rcti_size_y(rect); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -1872,6 +1882,7 @@ static void widget_draw_text(const uiFontStyle *fstyle, const char *drawstr = but->drawstr; const char *drawstr_right = nullptr; bool use_right_only = false; + const char *indeterminate_str = UI_VALUE_INDETERMINATE_CHAR; #ifdef WITH_INPUT_IME const wmIMEData *ime_data; @@ -1927,6 +1938,20 @@ static void widget_draw_text(const uiFontStyle *fstyle, } } + /* If not editing and indeterminate, show dash.*/ + if (but->drawflag & UI_BUT_INDETERMINATE && !but->editstr && + ELEM(but->type, + UI_BTYPE_MENU, + UI_BTYPE_NUM, + UI_BTYPE_NUM_SLIDER, + UI_BTYPE_TEXT, + UI_BTYPE_SEARCH_MENU)) + { + drawstr = indeterminate_str; + drawstr_left_len = strlen(drawstr); + align = UI_STYLE_TEXT_CENTER; + } + /* text button selection, cursor, composite underline */ if (but->editstr && but->pos != -1) { int but_pos_ofs; @@ -3775,7 +3800,7 @@ static void widget_numslider(uiBut *but, widgetbase_draw(&wtb, wcol); /* Draw slider part only when not in text editing. */ - if (!state->is_text_input) { + if (!state->is_text_input && !(but->drawflag & UI_BUT_INDETERMINATE)) { int roundboxalign_slider = roundboxalign; uchar outline[3]; @@ -3902,6 +3927,10 @@ static void widget_swatch(uiBut *but, ui_but_v3_get(but, col); + if (but->drawflag & UI_BUT_INDETERMINATE) { + col[0] = col[1] = col[2] = col[3] = 0.5f; + } + if ((state->but_flag & (UI_BUT_ANIMATED | UI_BUT_ANIMATED_KEY | UI_BUT_DRIVEN | UI_BUT_OVERRIDDEN | UI_BUT_REDALERT)) || (state->but_drawflag & UI_BUT_ANIMATED_CHANGED)) @@ -4210,11 +4239,19 @@ static void widget_optionbut(uiWidgetColors *wcol, /* Keep one edge in place. */ BLI_rcti_translate(&recttemp, text_before_widget ? delta : -delta, 0); + if (state->but_drawflag & UI_BUT_INDETERMINATE) { + /* The same muted background color regardless of state. */ + color_blend_v4_v4v4(wcol->inner, wcol->inner, wcol->inner_sel, 0.75f); + } + const float rad = widget_radius_from_rcti(&recttemp, wcol); round_box_edges(&wtb, UI_CNR_ALL, &recttemp, rad); /* decoration */ - if (state->but_flag & UI_SELECT) { + if (state->but_drawflag & UI_BUT_INDETERMINATE) { + shape_preset_trias_from_rect_dash(&wtb.tria1, &recttemp); + } + else if (state->but_flag & UI_SELECT) { shape_preset_trias_from_rect_checkmark(&wtb.tria1, &recttemp); } @@ -5032,6 +5069,10 @@ void ui_draw_but(const bContext *C, ARegion *region, uiStyle *style, uiBut *but, state.but_flag &= ~UI_BUT_OVERRIDDEN; } + if (state.but_drawflag & UI_BUT_INDETERMINATE) { + state.but_flag &= ~UI_SELECT; + } + const float zoom = 1.0f / but->block->aspect; wt->state(wt, &state, but->emboss); if (wt->custom) { diff --git a/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl index d6c93df9849..e556b389240 100644 --- a/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl +++ b/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl @@ -137,7 +137,7 @@ vec2 do_tria() outRectSize = vec2(0.74, 0.14); outRoundCorners = vec4(0.01); } - else { + else if (triaType == 5.0) { /* ROUNDBOX_TRIA_HOLD_ACTION_ARROW */ /* We use a single triangle to cut the round rect in half. The edge will not be Antialiased. */ pos = tria2 ? vec2(0.0) : arrow_pos[min(vidx, 2)]; /* Only keep 1 triangle. */ @@ -149,6 +149,14 @@ vec2 do_tria() outRectSize = vec2(0.75); outRoundCorners = vec4(0.01); } + else if (triaType == 6.0) { + /* ROUNDBOX_TRIA_DASH */ + pos = point_pos[vidx]; + uvInterp = point_uvs[vidx]; + uvInterp -= vec2(0.2, 0.45); /* Translate */ + outRectSize = vec2(0.6, 0.1); + outRoundCorners = vec4(0.01); + } uvInterp *= abs(size); outRectSize *= abs(size);