UI: IDSearch Image Thumbnail Tooltips

Hover tooltips for the Data-block ID selector showing previews for
images, movies, and fonts and also showing extra details.

Pull Request: https://projects.blender.org/blender/blender/pulls/118945
This commit is contained in:
Harley Acheson
2024-06-08 22:41:58 +02:00
committed by Harley Acheson
parent 79244e5632
commit 8937c0bcab
7 changed files with 272 additions and 49 deletions

View File

@@ -175,6 +175,15 @@ ImBuf *BKE_image_acquire_multilayer_view_ibuf(const RenderData &render_data,
void BKE_image_release_ibuf(struct Image *ima, struct ImBuf *ibuf, void *lock);
/**
* Return image buffer of preview for given image
* r_width & r_height are optional and return the _original size_ of the image.
*/
struct ImBuf *BKE_image_preview(struct Image *ima,
short max_size,
short *r_width,
short *r_height);
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

@@ -4902,6 +4902,31 @@ bool BKE_image_has_ibuf(Image *ima, ImageUser *iuser)
return ibuf != nullptr;
}
ImBuf *BKE_image_preview(Image *ima, const short max_size, short *r_width, short *r_height)
{
void *lock;
ImBuf *image_ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock);
if (image_ibuf == nullptr) {
return nullptr;
}
ImBuf *preview = IMB_dupImBuf(image_ibuf);
float scale = float(max_size) / float(std::max(image_ibuf->x, image_ibuf->y));
if (r_width) {
*r_width = image_ibuf->x;
}
if (r_height) {
*r_height = 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

@@ -3322,15 +3322,6 @@ ARegion *UI_tooltip_create_from_button_or_extra_icon(
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];
};
/**
* Create a tooltip from search-item tooltip data \a item_tooltip data.
* To be called from a callback set with #UI_but_func_search_set_tooltip().
@@ -3338,11 +3329,10 @@ struct uiSearchItemTooltipData {
* \param item_rect: Rectangle of the search item in search region space (#ui_searchbox_butrect())
* which is passed to the tooltip callback.
*/
ARegion *UI_tooltip_create_from_search_item_generic(
bContext *C,
const ARegion *searchbox_region,
const rcti *item_rect,
const uiSearchItemTooltipData *item_tooltip_data);
ARegion *UI_tooltip_create_from_search_item_generic(bContext *C,
const ARegion *searchbox_region,
const rcti *item_rect,
ID *id);
/* How long before a tool-tip shows. */
#define UI_TOOLTIP_DELAY 0.5

View File

@@ -31,28 +31,35 @@
#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_idtype.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"
@@ -1589,23 +1596,193 @@ ARegion *UI_tooltip_create_from_gizmo(bContext *C, wmGizmo *gz)
return ui_tooltip_create_with_data(C, data, init_position, nullptr, aspect);
}
static uiTooltipData *ui_tooltip_data_from_search_item_tooltip_data(
const uiSearchItemTooltipData *item_tooltip_data)
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 = static_cast<ImBufAnim *>(ima.anims.first);
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(ID *id)
{
uiTooltipData *data = MEM_new<uiTooltipData>(__func__);
const ID_Type type_id = GS(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, id->name + 2, {}, UI_TIP_STYLE_HEADER, UI_TIP_LC_MAIN);
if (type_id == ID_IM) {
ui_tooltip_from_image(*reinterpret_cast<Image *>(id), *data);
}
else if (type_id == ID_MC) {
ui_tooltip_from_clip(*reinterpret_cast<MovieClip *>(id), *data);
}
else if (type_id == ID_VF) {
ui_tooltip_from_vfont(*reinterpret_cast<VFont *>(id), *data);
}
else {
UI_tooltip_text_field_add(data,
fmt::format(TIP_("Choose {} data-block to be assigned to this user"),
BKE_idtype_idcode_to_name(GS(id->name))),
{},
UI_TIP_STYLE_NORMAL,
UI_TIP_LC_NORMAL);
}
if (item_tooltip_data->name && item_tooltip_data->name[0]) {
/** Additional info about the item (e.g. library name of a linked data-block). */
if (ID_IS_LINKED(id)) {
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);
data,
fmt::format(TIP_("Source library: {}\n{}"), id->lib->id.name + 2, id->lib->filepath),
{},
UI_TIP_STYLE_NORMAL,
UI_TIP_LC_NORMAL);
}
if (data->fields.is_empty()) {
@@ -1615,13 +1792,12 @@ static uiTooltipData *ui_tooltip_data_from_search_item_tooltip_data(
return data;
}
ARegion *UI_tooltip_create_from_search_item_generic(
bContext *C,
const ARegion *searchbox_region,
const rcti *item_rect,
const uiSearchItemTooltipData *item_tooltip_data)
ARegion *UI_tooltip_create_from_search_item_generic(bContext *C,
const ARegion *searchbox_region,
const rcti *item_rect,
ID *id)
{
uiTooltipData *data = ui_tooltip_data_from_search_item_tooltip_data(item_tooltip_data);
uiTooltipData *data = ui_tooltip_data_from_search_item_tooltip_data(id);
if (data == nullptr) {
return nullptr;
}

View File

@@ -498,26 +498,10 @@ static void id_search_cb_objects_from_scene(const bContext *C,
}
static ARegion *template_ID_search_menu_item_tooltip(
bContext *C, ARegion *region, const rcti *item_rect, void *arg, void *active)
bContext *C, ARegion *region, const rcti *item_rect, void * /*arg*/, void *active)
{
TemplateID *template_ui = static_cast<TemplateID *>(arg);
ID *active_id = static_cast<ID *>(active);
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);
}
return UI_tooltip_create_from_search_item_generic(C, region, item_rect, &tooltip_data);
return UI_tooltip_create_from_search_item_generic(C, region, item_rect, active_id);
}
/* ID Search browse menu, open */

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,40 @@ 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, 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, nullptr);
if (font_id != 0) {
BLF_unload_id(font_id);
}
return ibuf;
}