diff options
Diffstat (limited to 'source/blender/editors/interface/interface_widgets.c')
-rw-r--r-- | source/blender/editors/interface/interface_widgets.c | 347 |
1 files changed, 291 insertions, 56 deletions
diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 1a614bc7012..6d497fa474c 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -33,6 +33,7 @@ #include <string.h> #include <assert.h> +#include "DNA_brush_types.h" #include "DNA_screen_types.h" #include "DNA_userdef_types.h" @@ -845,7 +846,7 @@ static void widget_draw_icon(const uiBut *but, BIFIconID icon, float alpha, cons height = ICON_DEFAULT_HEIGHT / aspect; /* calculate blend color */ - if (ELEM4(but->type, TOG, ROW, TOGN, LISTROW)) { + if (ELEM(but->type, TOG, ROW, TOGN, LISTROW)) { if (but->flag & UI_SELECT) {} else if (but->flag & UI_ACTIVE) {} else alpha = 0.5f; @@ -918,45 +919,12 @@ static void ui_text_clip_give_next_off(uiBut *but, const char *str) but->ofs += bytes; } -/** - * Cut off the start of the text to fit into the width of \a rect - * - * \note Sets but->ofs to make sure text is correctly visible. - * \note Clips right in some cases, this function could be cleaned up. - */ -static void ui_text_clip_left(uiFontStyle *fstyle, uiBut *but, const rcti *rect) -{ - /* We are not supposed to use labels with that clipping, so we can always apply margins. */ - const int border = (int)(UI_TEXT_CLIP_MARGIN + 0.5f); - const int okwidth = max_ii(BLI_rcti_size_x(rect) - border, 0); - - /* need to set this first */ - uiStyleFontSet(fstyle); - - if (fstyle->kerning == 1) /* for BLF_width */ - BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - - but->ofs = 0; - but->strwidth = BLF_width(fstyle->uifont_id, but->drawstr, sizeof(but->drawstr)); - - if ((okwidth > 0.0f) && (but->strwidth > okwidth)) { - float strwidth; - but->ofs = BLF_width_to_rstrlen(fstyle->uifont_id, but->drawstr, - sizeof(but->drawstr), okwidth, &strwidth); - but->strwidth = strwidth; - } - - if (fstyle->kerning == 1) { - BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - } -} - /* Helper. * This func assumes things like kerning handling have already been handled! * Return the length of modified (right-clipped + ellipsis) string. */ static void ui_text_clip_right_ex(uiFontStyle *fstyle, char *str, const size_t max_len, const float okwidth, - const char *sep, const int sep_len, const float sep_strwidth) + const char *sep, const int sep_len, const float sep_strwidth, size_t *r_final_len) { float tmp; int l_end; @@ -969,19 +937,27 @@ static void ui_text_clip_right_ex(uiFontStyle *fstyle, char *str, const size_t m if (sep_strwidth / okwidth > 0.2f) { l_end = BLF_width_to_strlen(fstyle->uifont_id, str, max_len, okwidth, &tmp); str[l_end] = '\0'; + if (r_final_len) { + *r_final_len = (size_t)l_end; + } } else { l_end = BLF_width_to_strlen(fstyle->uifont_id, str, max_len, okwidth - sep_strwidth, &tmp); memcpy(str + l_end, sep, sep_len + 1); /* +1 for trailing '\0'. */ + if (r_final_len) { + *r_final_len = (size_t)(l_end + sep_len); + } } } /** * Cut off the middle of the text to fit into the given width. * Note in case this middle clipping would just remove a few chars, it rather clips right, which is more readable. + * If rpart_sep is not Null, the part of str starting to first occurrence of rpart_sep is preserved at all cost (useful + * for strings with shortcuts, like 'AVeryLongFooBarLabelForMenuEntry|Ctrl O' -> 'AVeryLong...MenuEntry|Ctrl O'). */ -static float ui_text_clip_middle_ex(uiFontStyle *fstyle, char *str, const float okwidth, const float minwidth, - const size_t max_len) +static float ui_text_clip_middle_ex(uiFontStyle *fstyle, char *str, float okwidth, const float minwidth, + const size_t max_len, const char *rpart_sep) { float strwidth; @@ -1000,37 +976,76 @@ static float ui_text_clip_middle_ex(uiFontStyle *fstyle, char *str, const float /* utf8 ellipsis '...', some compilers complain */ const char sep[] = {0xe2, 0x80, 0xa6, 0x0}; const int sep_len = sizeof(sep) - 1; + const float sep_strwidth = BLF_width(fstyle->uifont_id, sep, sep_len + 1); + float parts_strwidth; size_t l_end; - const float sep_strwidth = BLF_width(fstyle->uifont_id, sep, sep_len + 1); - const float parts_strwidth = ((float)okwidth - sep_strwidth) / 2.0f; + char *rpart = NULL, rpart_buf[UI_MAX_DRAW_STR]; + float rpart_width = 0.0f; + size_t rpart_len = 0; + size_t final_lpart_len; + + if (rpart_sep) { + rpart = strstr(str, rpart_sep); + + if (rpart) { + rpart_len = strlen(rpart); + rpart_width = BLF_width(fstyle->uifont_id, rpart, rpart_len); + okwidth -= rpart_width; + strwidth -= rpart_width; + + if (okwidth < 0.0f) { + /* Not enough place for actual label, just display protected right part. + * Here just for safety, should never happen in real life! */ + memmove(str, rpart, rpart_len + 1); + rpart = NULL; + okwidth += rpart_width; + strwidth = rpart_width; + } + } + } + + parts_strwidth = (okwidth - sep_strwidth) / 2.0f; + + if (rpart) { + strcpy(rpart_buf, rpart); + *rpart = '\0'; + rpart = rpart_buf; + } - if (min_ff(parts_strwidth, strwidth - okwidth) < minwidth) { + l_end = BLF_width_to_strlen(fstyle->uifont_id, str, max_len, parts_strwidth, &rpart_width); + if (l_end < 10 || min_ff(parts_strwidth, strwidth - okwidth) < minwidth) { /* If we really have no place, or we would clip a very small piece of string in the middle, * only show start of string. */ - ui_text_clip_right_ex(fstyle, str, max_len, okwidth, sep, sep_len, sep_strwidth); + ui_text_clip_right_ex(fstyle, str, max_len, okwidth, sep, sep_len, sep_strwidth, &final_lpart_len); } else { size_t r_offset, r_len; - l_end = BLF_width_to_strlen(fstyle->uifont_id, str, max_len, parts_strwidth, &strwidth); - r_offset = BLF_width_to_rstrlen(fstyle->uifont_id, str, max_len, parts_strwidth, &strwidth); - r_len = strlen(str + r_offset) + 1; /* +1 for the trailing '\0'... */ + r_offset = BLF_width_to_rstrlen(fstyle->uifont_id, str, max_len, parts_strwidth, &rpart_width); + r_len = strlen(str + r_offset) + 1; /* +1 for the trailing '\0'. */ - if (l_end + sep_len + r_len > max_len) { + if (l_end + sep_len + r_len + rpart_len > max_len) { /* Corner case, the str already takes all available mem, and the ellipsis chars would actually * add more chars... * Better to just trim one or two letters to the right in this case... * Note: with a single-char ellipsis, this should never happen! But better be safe here... */ - ui_text_clip_right_ex(fstyle, str, max_len, okwidth, sep, sep_len, sep_strwidth); + ui_text_clip_right_ex(fstyle, str, max_len, okwidth, sep, sep_len, sep_strwidth, &final_lpart_len); } else { memmove(str + l_end + sep_len, str + r_offset, r_len); memcpy(str + l_end, sep, sep_len); + final_lpart_len = (size_t)(l_end + sep_len + r_len - 1); /* -1 to remove trailing '\0'! */ } } + + if (rpart) { + /* Add back preserved right part to our shorten str. */ + memcpy(str + final_lpart_len, rpart, rpart_len + 1); /* +1 for trailing '\0'. */ + } + strwidth = BLF_width(fstyle->uifont_id, str, max_len); } @@ -1041,6 +1056,9 @@ static float ui_text_clip_middle_ex(uiFontStyle *fstyle, char *str, const float return strwidth; } +/** + * Wrapper around ui_text_clip_middle_ex. + */ static void ui_text_clip_middle(uiFontStyle *fstyle, uiBut *but, const rcti *rect) { /* No margin for labels! */ @@ -1050,7 +1068,23 @@ static void ui_text_clip_middle(uiFontStyle *fstyle, uiBut *but, const rcti *rec const float minwidth = (float)(UI_DPI_ICON_SIZE) / but->block->aspect * 2.0f; but->ofs = 0; - but->strwidth = ui_text_clip_middle_ex(fstyle, but->drawstr, okwidth, minwidth, max_len); + but->strwidth = ui_text_clip_middle_ex(fstyle, but->drawstr, okwidth, minwidth, max_len, NULL); +} + +/** + * Like ui_text_clip_middle(), but protect/preserve at all cost the right part of the string after sep. + * Useful for strings with shortcuts (like 'AVeryLongFooBarLabelForMenuEntry|Ctrl O' -> 'AVeryLong...MenuEntry|Ctrl O'). + */ +static void ui_text_clip_middle_protect_right(uiFontStyle *fstyle, uiBut *but, const rcti *rect, const char *rsep) +{ + /* No margin for labels! */ + const int border = ELEM(but->type, LABEL, MENU) ? 0 : (int)(UI_TEXT_CLIP_MARGIN + 0.5f); + const float okwidth = (float)max_ii(BLI_rcti_size_x(rect) - border, 0); + const size_t max_len = sizeof(but->drawstr); + const float minwidth = (float)(UI_DPI_ICON_SIZE) / but->block->aspect * 2.0f; + + but->ofs = 0; + but->strwidth = ui_text_clip_middle_ex(fstyle, but->drawstr, okwidth, minwidth, max_len, rsep); } /** @@ -1427,14 +1461,9 @@ static void widget_draw_text_icon(uiFontStyle *fstyle, uiWidgetColors *wcol, uiB else if (ELEM(but->type, NUM, NUMSLI)) { ui_text_clip_right_label(fstyle, but, rect); } -#if 0 - /* Special hack for non-embossed TEX buttons in uiList (we want them to behave as much as possible as labels). */ - else if ((but->type == TEX) && (but->flag & UI_BUT_LIST_ITEM) && (but->dt & UI_EMBOSSN)) { - but->ofs = 0; - } -#endif else if ((but->block->flag & UI_BLOCK_LOOP) && (but->type == BUT)) { - ui_text_clip_left(fstyle, but, rect); + /* Clip middle, but protect in all case right part containing the shortcut, if any. */ + ui_text_clip_middle_protect_right(fstyle, but, rect, "|"); } else { ui_text_clip_middle(fstyle, but, rect); @@ -1592,6 +1621,21 @@ static struct uiWidgetColors wcol_menu_back = { 25, -20 }; +/* pie menus */ +static struct uiWidgetColors wcol_pie_menu = { + {10, 10, 10, 200}, + {25, 25, 25, 230}, + {140, 140, 140, 255}, + {45, 45, 45, 230}, + + {160, 160, 160, 255}, + {255, 255, 255, 255}, + + 1, + 10, -10 +}; + + /* tooltip color */ static struct uiWidgetColors wcol_tooltip = { {0, 0, 0, 255}, @@ -1739,6 +1783,7 @@ void ui_widget_color_init(ThemeUI *tui) tui->wcol_menu = wcol_menu; tui->wcol_pulldown = wcol_pulldown; tui->wcol_menu_back = wcol_menu_back; + tui->wcol_pie_menu = wcol_pie_menu; tui->wcol_tooltip = wcol_tooltip; tui->wcol_menu_item = wcol_menu_item; tui->wcol_box = wcol_box; @@ -1887,6 +1932,34 @@ static void widget_state_pulldown(uiWidgetType *wt, int state) copy_v3_v3_char(wt->wcol.text, wt->wcol.text_sel); } +/* special case, pie menu items */ +static void widget_state_pie_menu_item(uiWidgetType *wt, int state) +{ + wt->wcol = *(wt->wcol_theme); + + /* active and disabled (not so common) */ + if ((state & UI_BUT_DISABLED) && (state & UI_ACTIVE)) { + widget_state_blend(wt->wcol.text, wt->wcol.text_sel, 0.5f); + /* draw the backdrop at low alpha, helps navigating with keys + * when disabled items are active */ + copy_v4_v4_char(wt->wcol.inner, wt->wcol.item); + wt->wcol.inner[3] = 64; + } + /* regular disabled */ + else if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) { + widget_state_blend(wt->wcol.text, wt->wcol.inner, 0.5f); + } + /* regular active */ + else if (state & UI_SELECT) { + copy_v4_v4_char(wt->wcol.outline, wt->wcol.inner_sel); + copy_v3_v3_char(wt->wcol.text, wt->wcol.text_sel); + } + else if (state & UI_ACTIVE) { + copy_v4_v4_char(wt->wcol.inner, wt->wcol.item); + copy_v3_v3_char(wt->wcol.text, wt->wcol.text_sel); + } +} + /* special case, menu items */ static void widget_state_menu_item(uiWidgetType *wt, int state) { @@ -2822,6 +2895,17 @@ static void widget_swatch(uiBut *but, uiWidgetColors *wcol, rcti *rect, int stat widgetbase_draw(&wtb, wcol); + if (but->a1 == UI_PALETTE_COLOR && but->a2 == UI_PALETTE_COLOR_ACTIVE) { + float width = rect->xmax - rect->xmin; + float height = rect->ymax - rect->ymin; + + glColor4ubv((unsigned char *)wcol->outline); + glBegin(GL_TRIANGLES); + glVertex2f(rect->xmin + 0.1f * width, rect->ymin + 0.9f * height); + glVertex2f(rect->xmin + 0.1f * width, rect->ymin + 0.5f * height); + glVertex2f(rect->xmin + 0.5f * width, rect->ymin + 0.9f * height); + glEnd(); + } } static void widget_normal(uiBut *but, uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int UNUSED(roundboxalign)) @@ -2958,6 +3042,29 @@ static void widget_menu_itembut(uiWidgetColors *wcol, rcti *rect, int UNUSED(sta widgetbase_draw(&wtb, wcol); } +static void widget_menu_radial_itembut(uiBut *but, uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int UNUSED(roundboxalign)) +{ + uiWidgetBase wtb; + float rad; + float fac = but->block->pie_data.alphafac; + + widget_init(&wtb); + + wtb.emboss = 0; + + rad = 0.5f * BLI_rcti_size_y(rect); + round_box_edges(&wtb, UI_CNR_ALL, rect, rad); + + wcol->inner[3] *= fac; + wcol->inner_sel[3] *= fac; + wcol->item[3] *= fac; + wcol->text[3] *= fac; + wcol->text_sel[3] *= fac; + wcol->outline[3] *= fac; + + widgetbase_draw(&wtb, wcol); +} + static void widget_list_itembut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int UNUSED(roundboxalign)) { uiWidgetBase wtb; @@ -3276,6 +3383,12 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type) wt.wcol_theme = &btheme->tui.wcol_progress; wt.custom = widget_progressbar; break; + + case UI_WTYPE_MENU_ITEM_RADIAL: + wt.wcol_theme = &btheme->tui.wcol_pie_menu; + wt.custom = widget_menu_radial_itembut; + wt.state = widget_state_pie_menu_item; + break; } return &wt; @@ -3382,6 +3495,9 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct /* "nothing" */ wt = widget_type(UI_WTYPE_ICON); } + else if (but->dt == UI_EMBOSSR) { + wt = widget_type(UI_WTYPE_MENU_ITEM_RADIAL); + } else { switch (but->type) { @@ -3634,6 +3750,125 @@ void ui_draw_menu_back(uiStyle *UNUSED(style), uiBlock *block, rcti *rect) } } +static void draw_disk_shaded( + float start, float angle, + float radius_int, float radius_ext, int subd, + const char col1[4], const char col2[4], + bool shaded) +{ + const float radius_ext_scale = (0.5f / radius_ext); /* 1 / (2 * radius_ext) */ + int i; + + float s, c; + float y1, y2; + float fac; + unsigned char r_col[4]; + + glBegin(GL_TRIANGLE_STRIP); + + s = sinf(start); + c = cosf(start); + + y1 = s * radius_int; + y2 = s * radius_ext; + + if (shaded) { + fac = (y1 + radius_ext) * radius_ext_scale; + round_box_shade_col4_r(r_col, col1, col2, fac); + + glColor4ubv(r_col); + } + + glVertex2f(c * radius_int, s * radius_int); + + if (shaded) { + fac = (y2 + radius_ext) * radius_ext_scale; + round_box_shade_col4_r(r_col, col1, col2, fac); + + glColor4ubv(r_col); + } + glVertex2f(c * radius_ext, s * radius_ext); + + for (i = 1; i < subd; i++) { + float a; + + a = start + ((i) / (float)(subd - 1)) * angle; + s = sinf(a); + c = cosf(a); + y1 = s * radius_int; + y2 = s * radius_ext; + + if (shaded) { + fac = (y1 + radius_ext) * radius_ext_scale; + round_box_shade_col4_r(r_col, col1, col2, fac); + + glColor4ubv(r_col); + } + glVertex2f(c * radius_int, s * radius_int); + + if (shaded) { + fac = (y2 + radius_ext) * radius_ext_scale; + round_box_shade_col4_r(r_col, col1, col2, fac); + + glColor4ubv(r_col); + } + glVertex2f(c * radius_ext, s * radius_ext); + } + glEnd(); + +} + +void ui_draw_pie_center(uiBlock *block) +{ + bTheme *btheme = UI_GetTheme(); + float cx = block->pie_data.pie_center_spawned[0]; + float cy = block->pie_data.pie_center_spawned[1]; + + float *pie_dir = block->pie_data.pie_dir; + + float pie_radius_internal = U.pixelsize * U.pie_menu_threshold; + float pie_radius_external = U.pixelsize * (U.pie_menu_threshold + 7.0f); + + int subd = 40; + + float angle = atan2(pie_dir[1], pie_dir[0]); + float range = (block->pie_data.flags & UI_PIE_DEGREES_RANGE_LARGE) ? ((float)M_PI / 2.0f) : ((float)M_PI / 4.0f); + + glPushMatrix(); + glTranslatef(cx, cy, 0.0f); + + glEnable(GL_BLEND); + if (btheme->tui.wcol_pie_menu.shaded) { + char col1[4], col2[4]; + shadecolors4(col1, col2, btheme->tui.wcol_pie_menu.inner, btheme->tui.wcol_pie_menu.shadetop, btheme->tui.wcol_pie_menu.shadedown); + draw_disk_shaded(0.0f, (float)(M_PI * 2.0), pie_radius_internal, pie_radius_external, subd, col1, col2, true); + } + else { + glColor4ubv((GLubyte *)btheme->tui.wcol_pie_menu.inner); + draw_disk_shaded(0.0f, (float)(M_PI * 2.0), pie_radius_internal, pie_radius_external, subd, NULL, NULL, false); + } + + if (!(block->pie_data.flags & UI_PIE_INVALID_DIR)) { + if (btheme->tui.wcol_pie_menu.shaded) { + char col1[4], col2[4]; + shadecolors4(col1, col2, btheme->tui.wcol_pie_menu.inner_sel, btheme->tui.wcol_pie_menu.shadetop, btheme->tui.wcol_pie_menu.shadedown); + draw_disk_shaded(angle - range / 2.0f, range, pie_radius_internal, pie_radius_external, subd, col1, col2, true); + } + else { + glColor4ubv((GLubyte *)btheme->tui.wcol_pie_menu.inner_sel); + draw_disk_shaded(angle - range / 2.0f, range, pie_radius_internal, pie_radius_external, subd, NULL, NULL, false); + } + } + + glColor4ubv((GLubyte *)btheme->tui.wcol_pie_menu.outline); + glutil_draw_lined_arc(0.0f, (float)M_PI * 2.0f, pie_radius_internal, subd); + glutil_draw_lined_arc(0.0f, (float)M_PI * 2.0f, pie_radius_external, subd); + + glDisable(GL_BLEND); + glPopMatrix(); +} + + uiWidgetColors *ui_tooltip_get_theme(void) { uiWidgetType *wt = widget_type(UI_WTYPE_TOOLTIP); @@ -3711,7 +3946,7 @@ void ui_draw_menu_item(uiFontStyle *fstyle, rcti *rect, const char *name, int ic const float minwidth = (float)(UI_DPI_ICON_SIZE); BLI_strncpy(drawstr, name, sizeof(drawstr)); - ui_text_clip_middle_ex(fstyle, drawstr, okwidth, minwidth, max_len); + ui_text_clip_middle_ex(fstyle, drawstr, okwidth, minwidth, max_len, NULL); glColor4ubv((unsigned char *)wt->wcol.text); uiStyleFontDraw(fstyle, rect, drawstr); @@ -3786,7 +4021,7 @@ void ui_draw_preview_item(uiFontStyle *fstyle, rcti *rect, const char *name, int const float minwidth = (float)(UI_DPI_ICON_SIZE); BLI_strncpy(drawstr, name, sizeof(drawstr)); - ui_text_clip_middle_ex(fstyle, drawstr, okwidth, minwidth, max_len); + ui_text_clip_middle_ex(fstyle, drawstr, okwidth, minwidth, max_len, NULL); glColor4ubv((unsigned char *)wt->wcol.text); uiStyleFontDraw(fstyle, &trect, drawstr); |