UI: Draw loading icon while previews load

Show a dimmed loading icon while previews are being loaded in a
background thread. The asset shelf and asset/file browsers do this
similarly already.

This is implemented in drawing code, so the loading icon will always
appear when an in-progress preview is being drawn. I experimented with
doing this in `ui_def_but_icon()`, but this won't update correctly with
popups that don't support full refreshing.

This also makes any normal icon that is drawn as preview use the normal
icon size. These icons are usually made for smaller sizes and look very
outblown when displayed at the size of a preview. Yet it's useful to
sometimes pass a normal icon. E.g. for the asset shelf we would already
draw the data-block type icon in place of the preview if there was no
preview to display, and we'd use the normal, smaller size already.
Larger can still be drawn differently.
I don't know of any current cases this would affect though.

Pull Request: https://projects.blender.org/blender/blender/pulls/133880
This commit is contained in:
Julian Eisel
2025-02-03 16:03:06 +01:00
committed by Julian Eisel
parent e283a7097c
commit 1f88645728
7 changed files with 83 additions and 74 deletions

View File

@@ -108,9 +108,6 @@ struct ImBuf *BKE_icon_imbuf_get_buffer(int icon_id) ATTR_WARN_UNUSED_RESULT;
*/
struct Icon *BKE_icon_get(int icon_id);
bool BKE_icon_is_preview(int icon_id);
bool BKE_icon_is_image(int icon_id);
/**
* Set icon for id if not already defined.
* Used for inserting the internal icons.

View File

@@ -413,18 +413,6 @@ Icon *BKE_icon_get(const int icon_id)
return icon;
}
bool BKE_icon_is_preview(const int icon_id)
{
const Icon *icon = BKE_icon_get(icon_id);
return icon != nullptr && icon->obj_type == ICON_DATA_PREVIEW;
}
bool BKE_icon_is_image(const int icon_id)
{
const Icon *icon = BKE_icon_get(icon_id);
return icon != nullptr && icon->obj_type == ICON_DATA_IMBUF;
}
void BKE_icon_set(const int icon_id, Icon *icon)
{
void **val_p;

View File

@@ -1135,6 +1135,32 @@ void ui_icon_ensure_deferred(const bContext *C, const int icon_id, const bool bi
}
}
bool ui_icon_is_preview_deferred_loading(const int icon_id, const bool big)
{
const Icon *icon = BKE_icon_get(icon_id);
if (icon == nullptr) {
return false;
}
const DrawInfo *di = static_cast<DrawInfo *>(icon->drawinfo);
if (icon->drawinfo == nullptr) {
return false;
}
if (di->type == ICON_TYPE_PREVIEW) {
const ID *id = (icon->id_type != 0) ? static_cast<ID *>(icon->obj) : nullptr;
const PreviewImage *prv = id ? BKE_previewimg_id_get(id) :
static_cast<PreviewImage *>(icon->obj);
if (prv) {
const int size = big ? ICON_SIZE_PREVIEW : ICON_SIZE_ICON;
return (prv->flag[size] & PRV_RENDERING) != 0;
}
}
return false;
}
/**
* * Only call with valid pointer from UI_icon_draw.
* * Only called when icon has changed.

View File

@@ -1326,8 +1326,7 @@ void ui_draw_preview_item_stateless(const uiFontStyle *fstyle,
blender::StringRef name,
int iconid,
const uchar text_col[4],
eFontStyle_Align text_align,
bool draw_as_icon = false);
eFontStyle_Align text_align);
#define UI_TEXT_MARGIN_X 0.4f
#define UI_POPUP_MARGIN (UI_SCALE_FAC * 12)
@@ -1351,6 +1350,8 @@ void uiStyleInit();
/* interface_icons.cc */
void ui_icon_ensure_deferred(const bContext *C, int icon_id, bool big);
/** Is \a icon_id a preview icon that is being loaded/rendered? */
bool ui_icon_is_preview_deferred_loading(int icon_id, bool big);
int ui_id_icon_get(const bContext *C, ID *id, bool big);
/* interface_icons_event.cc */

View File

@@ -160,6 +160,13 @@ int ui_but_icon(const uiBut *but)
return ICON_NONE;
}
const bool is_preview = (but->flag & UI_BUT_ICON_PREVIEW) != 0;
/* While icon is loading, show loading icon at the normal icon size. */
if (is_preview && ui_icon_is_preview_deferred_loading(but->icon, true)) {
return ICON_TEMP;
}
/* Consecutive icons can be toggle between. */
if (but->drawflag & UI_BUT_ICON_REVERSE) {
return but->icon - but->iconadd;

View File

@@ -1225,24 +1225,6 @@ static float widget_alpha_factor(const uiWidgetStateInfo *state)
return 1.0f;
}
static void widget_draw_preview(BIFIconID icon, float alpha, const rcti *rect)
{
if (icon == ICON_NONE) {
return;
}
const int w = BLI_rcti_size_x(rect);
const int h = BLI_rcti_size_y(rect);
const int size = std::min(w, h) - PREVIEW_PAD * 2;
if (size > 0) {
const int x = rect->xmin + w / 2 - size / 2;
const int y = rect->ymin + h / 2 - size / 2;
UI_icon_draw_preview(x, y, icon, 1.0f, alpha, size);
}
}
static void widget_draw_icon_centered(const BIFIconID icon,
const float aspect,
const float alpha,
@@ -1270,6 +1252,39 @@ static void widget_draw_icon_centered(const BIFIconID icon,
}
}
/**
* \param mono_color: Only for drawing monochrome icons.
*/
static void widget_draw_preview_icon(
BIFIconID icon, float alpha, float aspect, const rcti *rect, const uchar mono_color[4])
{
if (icon == ICON_NONE) {
return;
}
if (icon < BIFICONID_LAST_STATIC) {
const bool is_loading_icon = icon == ICON_TEMP;
/* Special handling: Previews often want to show a loading icon while the preview is being
* loaded. Draw this with reduced opacity. */
if (is_loading_icon) {
alpha *= 0.5f;
}
widget_draw_icon_centered(icon, aspect, alpha, rect, mono_color);
return;
}
const int w = BLI_rcti_size_x(rect);
const int h = BLI_rcti_size_y(rect);
const int size = std::min(w, h) - PREVIEW_PAD * 2;
if (size > 0) {
const int x = rect->xmin + w / 2 - size / 2;
const int y = rect->ymin + h / 2 - size / 2;
UI_icon_draw_preview(x, y, icon, 1.0f, alpha, size);
}
}
static int ui_but_draw_menu_icon(const uiBut *but)
{
return (but->flag & UI_BUT_ICON_SUBMENU) && (but->emboss == UI_EMBOSS_PULLDOWN);
@@ -1280,9 +1295,11 @@ static int ui_but_draw_menu_icon(const uiBut *but)
static void widget_draw_icon(
const uiBut *but, BIFIconID icon, float alpha, const rcti *rect, const uchar mono_color[4])
{
const float aspect = but->block->aspect * UI_INV_SCALE_FAC;
if (but->flag & UI_BUT_ICON_PREVIEW) {
GPU_blend(GPU_BLEND_ALPHA);
widget_draw_preview(icon, alpha, rect);
widget_draw_preview_icon(icon, alpha, aspect, rect, mono_color);
GPU_blend(GPU_BLEND_NONE);
return;
}
@@ -1292,7 +1309,6 @@ static void widget_draw_icon(
return;
}
const float aspect = but->block->aspect * UI_INV_SCALE_FAC;
const float height = ICON_DEFAULT_HEIGHT / aspect;
/* calculate blend color */
@@ -2251,11 +2267,14 @@ static void widget_draw_text_icon(const uiFontStyle *fstyle,
widget_draw_node_link_socket(wcol, &temp, but, alpha);
}
const uchar *icon_color = (but->col[3] != 0) ? but->col : wcol->text;
/* If there's an icon too (made with uiDefIconTextBut) then draw the icon
* and offset the text label to accommodate it */
/* Big previews with optional text label below */
if (but->flag & UI_BUT_ICON_PREVIEW && ui_block_is_menu(but->block)) {
const float aspect = but->block->aspect * UI_INV_SCALE_FAC;
const BIFIconID icon = ui_but_icon(but);
int icon_size = BLI_rcti_size_y(rect);
int text_size = 0;
@@ -2270,7 +2289,7 @@ static void widget_draw_text_icon(const uiFontStyle *fstyle,
/* draw icon in rect above the space reserved for the label */
rect->ymin += text_size;
GPU_blend(GPU_BLEND_ALPHA);
widget_draw_preview(icon, alpha, rect);
widget_draw_preview_icon(icon, alpha, aspect, rect, icon_color);
GPU_blend(GPU_BLEND_NONE);
/* offset rect to draw label in */
@@ -2322,7 +2341,7 @@ static void widget_draw_text_icon(const uiFontStyle *fstyle,
}
/* By default icon is the color of text, but can optionally override with but->col. */
widget_draw_icon(but, icon, alpha, rect, (but->col[3] != 0) ? but->col : wcol->text);
widget_draw_icon(but, icon, alpha, rect, icon_color);
if (show_menu_icon) {
BLI_assert(but->block->content_hints & UI_BLOCK_CONTAINS_SUBMENU_BUT);
@@ -4241,17 +4260,9 @@ static void widget_preview_tile(uiBut *but,
widget_list_itembut(but, wcol, rect, state, roundboxalign, zoom);
}
/* When the button is not tagged as having a preview icon, do regular icon drawing with the
* standard icon size. */
const bool draw_as_icon = !(but->flag & UI_BUT_ICON_PREVIEW);
ui_draw_preview_item_stateless(&UI_style_get()->widget,
rect,
but->drawstr,
but->icon,
wcol->text,
UI_STYLE_TEXT_CENTER,
draw_as_icon);
const BIFIconID icon = ui_but_icon(but);
ui_draw_preview_item_stateless(
&UI_style_get()->widget, rect, but->drawstr, icon, wcol->text, UI_STYLE_TEXT_CENTER);
}
static void widget_optionbut(uiWidgetColors *wcol,
@@ -5637,8 +5648,7 @@ void ui_draw_preview_item_stateless(const uiFontStyle *fstyle,
const blender::StringRef name,
int iconid,
const uchar text_col[4],
eFontStyle_Align text_align,
bool draw_as_icon)
eFontStyle_Align text_align)
{
rcti trect = *rect;
const float text_size = UI_UNIT_Y;
@@ -5646,27 +5656,12 @@ void ui_draw_preview_item_stateless(const uiFontStyle *fstyle,
float alpha = 1.0f;
{
/* Special handling: Previews often want to show a loading icon while the preview is being
* loaded. Draw this with reduced opacity. */
const bool is_loading_icon = iconid == ICON_TEMP;
if (is_loading_icon) {
alpha *= 0.5f;
draw_as_icon = true;
}
}
if (has_text) {
/* draw icon in rect above the space reserved for the label */
rect->ymin += text_size;
}
GPU_blend(GPU_BLEND_ALPHA);
if (draw_as_icon) {
widget_draw_icon_centered(iconid, 1.0f, alpha, rect, text_col);
}
else {
widget_draw_preview(iconid, alpha, rect);
}
widget_draw_preview_icon(iconid, alpha, 1.0f, rect, text_col);
GPU_blend(GPU_BLEND_NONE);
if (!has_text) {

View File

@@ -504,15 +504,10 @@ void PreviewGridItem::build_grid_tile_button(uiLayout &layout,
const BIFIconID icon_id = override_preview_icon_id ? override_preview_icon_id : preview_icon_id;
/* Draw icons that are not previews or images as normal icons with a fixed icon size. Otherwise
* they will be upscaled to the button size. Should probably be done by the widget code. */
const int is_preview_flag = (BKE_icon_is_preview(icon_id) || BKE_icon_is_image(icon_id)) ?
int(UI_BUT_ICON_PREVIEW) :
0;
ui_def_but_icon(but,
icon_id,
/* NOLINTNEXTLINE: bugprone-suspicious-enum-usage */
UI_HAS_ICON | is_preview_flag);
UI_HAS_ICON | UI_BUT_ICON_PREVIEW);
but->emboss = UI_EMBOSS_NONE;
}