UI: IDSearch Image Thumbnail Tooltips #118945

Open
Harley Acheson wants to merge 16 commits from Harley/blender:IDSearchImageThumbs into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
7 changed files with 271 additions and 29 deletions

View File

@ -153,6 +153,12 @@ bool BKE_image_has_ibuf(struct Image *ima, struct ImageUser *iuser);
struct ImBuf *BKE_image_acquire_ibuf(struct Image *ima, struct ImageUser *iuser, void **r_lock);
void BKE_image_release_ibuf(struct Image *ima, struct ImBuf *ibuf, void *lock);
/**
* Return image buffer of preview for given image
* width_r & *height_r are optional and return the _original size_ of the image.
*/
struct ImBuf *BKE_image_preview(struct Image *ima, short max, short *width_r, short *height_r);
struct ImagePool *BKE_image_pool_new(void);
void BKE_image_pool_free(struct ImagePool *pool);
struct ImBuf *BKE_image_pool_acquire_ibuf(struct Image *ima,

View File

@ -4800,6 +4800,31 @@ bool BKE_image_has_ibuf(Image *ima, ImageUser *iuser)
return ibuf != nullptr;
}
ImBuf *BKE_image_preview(struct Image *ima, short max, short *width_r, short *height_r)
{
void *lock;
ImBuf *image_ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock);
ImBuf *preview = nullptr;
if (image_ibuf) {
preview = IMB_dupImBuf(image_ibuf);
float scale = float(max) / float(std::max(image_ibuf->x, image_ibuf->y));
if (width_r) {
*width_r = image_ibuf->x;
}
if (height_r) {
*height_r = image_ibuf->y;
}
BKE_image_release_ibuf(ima, image_ibuf, lock);
/* Resize. */
IMB_scaleImBuf(preview, scale * image_ibuf->x, scale * image_ibuf->y);
IMB_rect_from_float(preview);
}
return preview;
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -3302,12 +3302,8 @@ ARegion *UI_tooltip_create_from_gizmo(bContext *C, wmGizmo *gz);
void UI_tooltip_free(bContext *C, bScreen *screen, ARegion *region);
struct uiSearchItemTooltipData {
/** A description for the item, e.g. what happens when selecting it. */
char description[UI_MAX_DRAW_STR];
/* The full name of the item, without prefixes or suffixes (e.g. hint with UI_SEP_CHARP). */
const char *name;
/** Additional info about the item (e.g. library name of a linked data-block). */
char hint[UI_MAX_DRAW_STR];
ID *id;
StructRNA *type;
};
/**

View File

@ -31,28 +31,34 @@
#include "BLI_listbase.h"
#include "BLI_math_color.h"
#include "BLI_math_vector.h"
#include "BLI_path_util.h"
#include "BLI_rect.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "BKE_context.hh"
#include "BKE_image.h"
#include "BKE_paint.hh"
#include "BKE_screen.hh"
#include "BIF_glutil.hh"
#include "DNA_vfont_types.h"
#include "GPU_immediate.hh"
#include "GPU_immediate_util.hh"
#include "GPU_state.hh"
#include "IMB_imbuf.hh"
#include "IMB_imbuf_types.hh"
#include "IMB_thumbs.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "RNA_access.hh"
#include "RNA_path.hh"
#include "RNA_prototypes.h"
#include "UI_interface.hh"
@ -1502,23 +1508,196 @@ ARegion *UI_tooltip_create_from_gizmo(bContext *C, wmGizmo *gz)
return ui_tooltip_create_with_data(C, data, init_position, nullptr, aspect);
}
static void ui_tooltip_from_image(Image *ima, uiTooltipData *data)
{
if (ima->filepath[0]) {
char root[FILE_MAX];
BLI_path_split_dir_part(ima->filepath, root, FILE_MAX);
UI_tooltip_text_field_add(data, root, {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_NORMAL);
}
std::string image_type;
switch (ima->source) {
case IMA_SRC_FILE:
image_type = TIP_("Single Image");
break;
case IMA_SRC_SEQUENCE:
image_type = TIP_("Image Sequence");
break;
case IMA_SRC_MOVIE:
image_type = TIP_("Movie");
break;
case IMA_SRC_GENERATED:
image_type = TIP_("Generated");
break;
case IMA_SRC_VIEWER:
image_type = TIP_("Viewer");
break;
case IMA_SRC_TILED:
image_type = TIP_("UDIM Tiles");
break;
}
UI_tooltip_text_field_add(data, image_type, {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_NORMAL);
short w;
short h;
ImBuf *ibuf = BKE_image_preview(ima, 200.0f * UI_SCALE_FAC, &w, &h);
if (ibuf) {
UI_tooltip_text_field_add(
data, fmt::format("{} \u00D7 {}", w, h), {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_NORMAL);
}
if (BKE_image_has_anim(ima)) {
ImBufAnim *anim = ((ImageAnim *)ima->anims.first)->anim;
if (anim) {
int duration = IMB_anim_get_duration(anim, IMB_TC_RECORD_RUN);
UI_tooltip_text_field_add(
data, fmt::format("Frames: {}", duration), {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_NORMAL);
}
}
UI_tooltip_text_field_add(
data, ima->colorspace_settings.name, {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_NORMAL);
UI_tooltip_text_field_add(
data, fmt::format(TIP_("Users: {}"), ima->id.us), {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_NORMAL);
if (ibuf) {
uiTooltipImage image_data;
image_data.width = int(ibuf->x);
image_data.height = int(ibuf->y);
image_data.ibuf = ibuf;
image_data.border = true;
image_data.background = uiTooltipImageBackground::Checkerboard_Themed;
image_data.premultiplied = true;
UI_tooltip_text_field_add(data, {}, {}, UI_TIP_STYLE_SPACER, UI_TIP_LC_NORMAL);
UI_tooltip_text_field_add(data, {}, {}, UI_TIP_STYLE_SPACER, UI_TIP_LC_NORMAL);
UI_tooltip_image_field_add(data, image_data);
}
}
static void ui_tooltip_from_clip(MovieClip *clip, uiTooltipData *data)
{
if (clip->filepath[0]) {
char root[FILE_MAX];
BLI_path_split_dir_part(clip->filepath, root, FILE_MAX);
UI_tooltip_text_field_add(data, root, {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_NORMAL);
}
std::string image_type;
switch (clip->source) {
case IMA_SRC_SEQUENCE:
image_type = TIP_("Image Sequence");
break;
case IMA_SRC_MOVIE:
image_type = TIP_("Movie");
break;
}
UI_tooltip_text_field_add(data, image_type, {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_NORMAL);
if (clip->anim) {
ImBufAnim *anim = clip->anim;
UI_tooltip_text_field_add(data,
fmt::format("{} \u00D7 {}",
IMB_anim_get_image_width(anim),
IMB_anim_get_image_height(anim)),
{},
UI_TIP_STYLE_NORMAL,
UI_TIP_LC_NORMAL);
UI_tooltip_text_field_add(
data,
fmt::format("Frames: {}", IMB_anim_get_duration(anim, IMB_TC_RECORD_RUN)),
{},
UI_TIP_STYLE_NORMAL,
UI_TIP_LC_NORMAL);
ImBuf *ibuf = IMB_anim_previewframe(anim);
if (ibuf) {
/* Resize. */
float scale = float(200.0f * UI_SCALE_FAC) / float(std::max(ibuf->x, ibuf->y));
IMB_scaleImBuf(ibuf, scale * ibuf->x, scale * ibuf->y);
IMB_rect_from_float(ibuf);
uiTooltipImage image_data;
image_data.width = int(ibuf->x);
image_data.height = int(ibuf->y);
image_data.ibuf = ibuf;
image_data.border = true;
image_data.background = uiTooltipImageBackground::Checkerboard_Themed;
image_data.premultiplied = true;
UI_tooltip_text_field_add(data, {}, {}, UI_TIP_STYLE_SPACER, UI_TIP_LC_NORMAL);
UI_tooltip_text_field_add(data, {}, {}, UI_TIP_STYLE_SPACER, UI_TIP_LC_NORMAL);
UI_tooltip_image_field_add(data, image_data);
IMB_freeImBuf(ibuf);
}
}
}
static void ui_tooltip_from_vfont(VFont *font, uiTooltipData *data)
{
if (!font->filepath[0]) {
/* Let's not bother with packed files _for now_.*/
return;
}
float color[4];
const uiWidgetColors *theme = ui_tooltip_get_theme();
rgba_uchar_to_float(color, theme->text);
ImBuf *ibuf = IMB_font_preview(font->filepath, 200 * UI_SCALE_FAC, color);
if (ibuf) {
uiTooltipImage image_data;
image_data.width = ibuf->x;
image_data.height = ibuf->y;
image_data.ibuf = ibuf;
image_data.border = false;
image_data.background = uiTooltipImageBackground::None;
image_data.premultiplied = false;
image_data.text_color = true;
UI_tooltip_image_field_add(data, image_data);
IMB_freeImBuf(ibuf);
}
}
static uiTooltipData *ui_tooltip_data_from_search_item_tooltip_data(
const uiSearchItemTooltipData *item_tooltip_data)
bContext *C, const uiSearchItemTooltipData *item_tooltip_data)
{
uiTooltipData *data = MEM_new<uiTooltipData>(__func__);
const ID_Type type_id = GS(item_tooltip_data->id->name);
if (item_tooltip_data->description[0]) {
UI_tooltip_text_field_add(
data, item_tooltip_data->description, {}, UI_TIP_STYLE_HEADER, UI_TIP_LC_NORMAL, true);
UI_tooltip_text_field_add(
data, item_tooltip_data->id->name + 2, {}, UI_TIP_STYLE_HEADER, UI_TIP_LC_MAIN);
if (type_id == ID_IM) {
ui_tooltip_from_image(reinterpret_cast<Image *>(item_tooltip_data->id), data);
}
else if (type_id == ID_MC) {
ui_tooltip_from_clip(reinterpret_cast<MovieClip *>(item_tooltip_data->id), data);
}
else if (type_id == ID_VF) {
ui_tooltip_from_vfont(reinterpret_cast<VFont *>(item_tooltip_data->id), data);
}
else {
UI_tooltip_text_field_add(data,
fmt::format(TIP_("Choose {} data-block to be assigned to this user"),
RNA_struct_ui_name(item_tooltip_data->type)),
{},
UI_TIP_STYLE_NORMAL,
UI_TIP_LC_NORMAL);
}
if (item_tooltip_data->name && item_tooltip_data->name[0]) {
UI_tooltip_text_field_add(
data, item_tooltip_data->name, {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_VALUE, true);
}
if (item_tooltip_data->hint[0]) {
UI_tooltip_text_field_add(
data, item_tooltip_data->hint, {}, UI_TIP_STYLE_NORMAL, UI_TIP_LC_NORMAL, true);
/** Additional info about the item (e.g. library name of a linked data-block). */
if (ID_IS_LINKED(item_tooltip_data->id)) {
UI_tooltip_text_field_add(data,
fmt::format(TIP_("Source library: {}\n{}"),
item_tooltip_data->id->lib->id.name + 2,
item_tooltip_data->id->lib->filepath),
{},
UI_TIP_STYLE_NORMAL,
UI_TIP_LC_NORMAL);
}
if (data->fields.is_empty()) {
@ -1534,7 +1713,7 @@ ARegion *UI_tooltip_create_from_search_item_generic(
const rcti *item_rect,
const uiSearchItemTooltipData *item_tooltip_data)
{
uiTooltipData *data = ui_tooltip_data_from_search_item_tooltip_data(item_tooltip_data);
uiTooltipData *data = ui_tooltip_data_from_search_item_tooltip_data(C, item_tooltip_data);
if (data == nullptr) {
return nullptr;
}

View File

@ -504,17 +504,8 @@ static ARegion *template_ID_search_menu_item_tooltip(
StructRNA *type = RNA_property_pointer_type(&template_ui->ptr, template_ui->prop);
uiSearchItemTooltipData tooltip_data = {{0}};
tooltip_data.name = active_id->name + 2;
SNPRINTF(tooltip_data.description,
TIP_("Choose %s data-block to be assigned to this user"),
RNA_struct_ui_name(type));
if (ID_IS_LINKED(active_id)) {
SNPRINTF(tooltip_data.hint,
TIP_("Source library: %s\n%s"),
active_id->lib->id.name + 2,
active_id->lib->filepath);
}
tooltip_data.id = active_id;
tooltip_data.type = type;
return UI_tooltip_create_from_search_item_generic(C, region, item_rect, &tooltip_data);
}

View File

@ -91,6 +91,8 @@ ImBuf *IMB_thumb_load_blend(const char *blen_path, const char *blen_group, const
ImBuf *IMB_thumb_load_font(const char *filename, unsigned int x, unsigned int y);
bool IMB_thumb_load_font_get_hash(char *r_hash);
ImBuf *IMB_font_preview(const char *filename, unsigned int width, float color[4]);
/* Threading */
void IMB_thumb_locks_acquire();

View File

@ -45,3 +45,46 @@ bool IMB_thumb_load_font_get_hash(char *r_hash)
return true;
}
ImBuf *IMB_font_preview(const char *filename, unsigned int width, float color[4])
{
int font_id = (filename[0] != '<') ? BLF_load(filename) : 0;
const char sample[] = "ABCDEFGH\nabcdefg123";
BLF_buffer_col(font_id, color);
BLF_size(font_id, 50.0f);
BLF_enable(font_id, BLF_WORD_WRAP);
float name_w;
float name_h;
BLF_width_and_height(font_id, sample, sizeof(sample), &name_w, &name_h);
float scale = float(width) / name_w;
BLF_size(font_id, scale * 50.0f);
name_w *= scale;
name_h *= scale;
int height = int(name_h * 1.3f);
ImBuf *ibuf = IMB_allocImBuf(width, height, 32, IB_rect);
/* fill with white and zero alpha */
const float col[4] = {1.0f, 1.0f, 1.0f, 0.0f};
IMB_rectfill(ibuf, col);
BLF_buffer(font_id,
ibuf->float_buffer.data,
ibuf->byte_buffer.data,
width,
height,
ibuf->channels,
nullptr);
BLF_position(font_id, 0.0f, name_h * 0.8f, 0.0f);
BLF_draw_buffer(font_id, sample, 1024);
BLF_buffer(font_id, nullptr, nullptr, 0, 0, 0, nullptr);
if (font_id != 0) {
BLF_unload_id(font_id);
}
return ibuf;
}