UI: Dynamic File Browser Tooltips #104547
|
@ -534,6 +534,14 @@ void BLO_sanitize_experimental_features_userpref_blend(struct UserDef *userdef);
|
|||
*/
|
||||
struct BlendThumbnail *BLO_thumbnail_from_file(const char *filepath);
|
||||
|
||||
/**
|
||||
* Does a very light reading of given .blend file to extract its version.
|
||||
*
|
||||
* \param filepath: The path of the blend file to extract version from.
|
||||
* \return The file version
|
||||
*/
|
||||
short BLO_version_from_file(const char *filepath);
|
||||
|
||||
/** Default theme, see: `release/datafiles/userdef/userdef_default_theme.c`. */
|
||||
extern const struct bTheme U_theme_default;
|
||||
/** Default preferences, defined by: `release/datafiles/userdef/userdef_default.c`. */
|
||||
|
|
|
@ -1412,6 +1412,17 @@ BlendThumbnail *BLO_thumbnail_from_file(const char *filepath)
|
|||
return data;
|
||||
}
|
||||
|
||||
short BLO_version_from_file(const char *filepath)
|
||||
{
|
||||
short version = 0;
|
||||
FileData *fd = blo_filedata_from_file_minimal(filepath);
|
||||
if (fd) {
|
||||
version = fd->fileversion;
|
||||
blo_filedata_free(fd);
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <cerrno>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
|
@ -29,11 +30,16 @@
|
|||
#include "BKE_context.hh"
|
||||
#include "BKE_report.h"
|
||||
|
||||
#include "BLO_readfile.h"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "BLF_api.h"
|
||||
|
||||
#include "IMB_imbuf.h"
|
||||
#include "IMB_imbuf_types.h"
|
||||
#include "IMB_metadata.h"
|
||||
#include "IMB_thumbs.h"
|
||||
|
||||
#include "DNA_userdef_types.h"
|
||||
#include "DNA_windowmanager_types.h"
|
||||
|
@ -108,11 +114,227 @@ void ED_file_path_button(bScreen *screen,
|
|||
UI_block_func_set(block, nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
/* Dummy helper - we need dynamic tooltips here. */
|
||||
static char *file_draw_tooltip_func(bContext * /*C*/, void *argN, const char * /*tip*/)
|
||||
struct file_tooltip_data {
|
||||
Harley marked this conversation as resolved
Outdated
Julian Eisel
commented
Outdated
Review
```c++
struct FileTooltipData {
const SpaceFile *sfile;
const FileDirEntry *file;
};
```
|
||||
const SpaceFile *sfile;
|
||||
const FileDirEntry *file;
|
||||
};
|
||||
|
||||
static file_tooltip_data *file_tooltip_data_create(const SpaceFile *sfile,
|
||||
const FileDirEntry *file)
|
||||
{
|
||||
char *dyn_tooltip = static_cast<char *>(argN);
|
||||
return BLI_strdup(dyn_tooltip);
|
||||
file_tooltip_data *data = (file_tooltip_data *)MEM_mallocN(sizeof(file_tooltip_data),
|
||||
"tooltip_data");
|
||||
data->sfile = sfile;
|
||||
data->file = file;
|
||||
return data;
|
||||
}
|
||||
|
||||
static void file_draw_tooltip_custom_func(bContext * /*C*/, struct uiTooltipData *tip, void *argN)
|
||||
{
|
||||
file_tooltip_data *file_data = static_cast<file_tooltip_data *>(argN);
|
||||
const SpaceFile *sfile = file_data->sfile;
|
||||
const FileList *files = sfile->files;
|
||||
const FileSelectParams *params = ED_fileselect_get_active_params(sfile);
|
||||
const FileDirEntry *file = file_data->file;
|
||||
|
||||
BLI_assert_msg(!file->asset, "Asset tooltip should never be overridden here.");
|
||||
|
||||
Harley marked this conversation as resolved
Outdated
Julian Eisel
commented
I'd rather assert here, since when this is called, it would mean any asset tooltip callback was overridden by a file one. I'd rather assert here, since when this is called, it would mean any asset tooltip callback was overridden by a file one.
|
||||
/* Check the FileDirEntry first to see if the preview is already loaded. */
|
||||
ImBuf *thumb = filelist_file_getimage(file);
|
||||
|
||||
/* Only free if it is loaded later. */
|
||||
bool free_imbuf = (thumb == nullptr);
|
||||
|
||||
UI_tooltip_text_field_add(
|
||||
tip, BLI_strdup(file->name), nullptr, UI_TIP_STYLE_HEADER, UI_TIP_LC_MAIN);
|
||||
UI_tooltip_text_field_add(tip, nullptr, nullptr, UI_TIP_STYLE_SPACER, UI_TIP_LC_NORMAL);
|
||||
|
||||
if (!(file->typeflag & FILE_TYPE_BLENDERLIB)) {
|
||||
|
||||
char full_path[FILE_MAX_LIBEXTRA];
|
||||
filelist_file_get_full_path(files, file, full_path);
|
||||
|
||||
if (params->recursion_level > 0) {
|
||||
char root[FILE_MAX];
|
||||
BLI_path_split_dir_part(full_path, root, FILE_MAX);
|
||||
UI_tooltip_text_field_add(
|
||||
tip, BLI_strdup(root), nullptr, UI_TIP_STYLE_NORMAL, UI_TIP_LC_NORMAL);
|
||||
}
|
||||
|
||||
if (file->redirection_path) {
|
||||
UI_tooltip_text_field_add(tip,
|
||||
BLI_sprintfN("%s: %s", N_("Link target"), file->redirection_path),
|
||||
nullptr,
|
||||
UI_TIP_STYLE_NORMAL,
|
||||
UI_TIP_LC_NORMAL);
|
||||
}
|
||||
if (file->attributes & FILE_ATTR_OFFLINE) {
|
||||
UI_tooltip_text_field_add(tip,
|
||||
BLI_strdup(N_("This file is offline")),
|
||||
nullptr,
|
||||
UI_TIP_STYLE_NORMAL,
|
||||
UI_TIP_LC_ALERT);
|
||||
}
|
||||
if (file->attributes & FILE_ATTR_READONLY) {
|
||||
UI_tooltip_text_field_add(tip,
|
||||
BLI_strdup(N_("This file is read-only")),
|
||||
nullptr,
|
||||
UI_TIP_STYLE_NORMAL,
|
||||
UI_TIP_LC_ALERT);
|
||||
}
|
||||
if (file->attributes & (FILE_ATTR_SYSTEM | FILE_ATTR_RESTRICTED)) {
|
||||
UI_tooltip_text_field_add(tip,
|
||||
BLI_strdup(N_("This is a restricted system file")),
|
||||
nullptr,
|
||||
UI_TIP_STYLE_NORMAL,
|
||||
UI_TIP_LC_ALERT);
|
||||
}
|
||||
|
||||
if (file->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) {
|
||||
Julian Eisel
commented
Why is this only using Why is this only using `_read()`, instead of `_manage()`?
Harley Acheson
commented
In this case I don't want to create a new preview if one doesn't exist. Getting the version from the file is faster. In this case I don't want to create a new preview if one doesn't exist. Getting the version from the file is faster.
|
||||
char version_st[128] = {0};
|
||||
if (!thumb) {
|
||||
/* Load the thumbnail from cache if existing, but don't create if not. */
|
||||
thumb = IMB_thumb_read(full_path, THB_LARGE);
|
||||
}
|
||||
if (thumb) {
|
||||
/* Look for version in existing thumbnail if available. */
|
||||
Harley marked this conversation as resolved
Outdated
Brecht Van Lommel
commented
It's sightly suboptimal to open a file as part of tooltip drawing. On a slow network driver this could lead to a noticeable delay in display the tooltip when quickly scanning across multiple files quickly. It's sightly suboptimal to open a file as part of tooltip drawing. On a slow network driver this could lead to a noticeable delay in display the tooltip when quickly scanning across multiple files quickly.
|
||||
IMB_metadata_get_field(
|
||||
thumb->metadata, "Thumb::Blender::Version", version_st, sizeof(version_st));
|
||||
}
|
||||
|
||||
if (!version_st[0] && !(file->attributes & FILE_ATTR_OFFLINE)) {
|
||||
/* Load Blender version directly from the file. */
|
||||
short version = BLO_version_from_file(full_path);
|
||||
if (version != 0) {
|
||||
SNPRINTF(version_st, "%d.%01d", version / 100, version % 100);
|
||||
}
|
||||
}
|
||||
|
||||
if (version_st[0]) {
|
||||
UI_tooltip_text_field_add(tip,
|
||||
BLI_sprintfN("Blender %s", version_st),
|
||||
nullptr,
|
||||
UI_TIP_STYLE_NORMAL,
|
||||
Harley marked this conversation as resolved
Outdated
Julian Eisel
commented
`IMB_thumb_manage()` handles the case of offline files now, it won't attempt to generate those.
|
||||
UI_TIP_LC_NORMAL);
|
||||
UI_tooltip_text_field_add(tip, nullptr, nullptr, UI_TIP_STYLE_SPACER, UI_TIP_LC_NORMAL);
|
||||
}
|
||||
}
|
||||
else if (file->typeflag & FILE_TYPE_IMAGE) {
|
||||
if (!thumb) {
|
||||
/* Load the thumbnail from cache if existing, create if not. */
|
||||
thumb = IMB_thumb_manage(full_path, THB_LARGE, THB_SOURCE_IMAGE);
|
||||
}
|
||||
if (thumb) {
|
||||
char value1[128];
|
||||
char value2[128];
|
||||
if (IMB_metadata_get_field(
|
||||
thumb->metadata, "Thumb::Image::Width", value1, sizeof(value1)) &&
|
||||
IMB_metadata_get_field(
|
||||
thumb->metadata, "Thumb::Image::Height", value2, sizeof(value2)))
|
||||
{
|
||||
UI_tooltip_text_field_add(tip,
|
||||
BLI_sprintfN("%s \u00D7 %s", value1, value2),
|
||||
nullptr,
|
||||
UI_TIP_STYLE_NORMAL,
|
||||
Harley marked this conversation as resolved
Outdated
Julian Eisel
commented
Same here re offline file handling in Same here re offline file handling in `IMB_thumb_manage()`.
|
||||
UI_TIP_LC_NORMAL);
|
||||
UI_tooltip_text_field_add(tip, nullptr, nullptr, UI_TIP_STYLE_SPACER, UI_TIP_LC_NORMAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (file->typeflag & FILE_TYPE_MOVIE) {
|
||||
if (!thumb) {
|
||||
/* This could possibly take a while. */
|
||||
thumb = IMB_thumb_manage(full_path, THB_LARGE, THB_SOURCE_MOVIE);
|
||||
}
|
||||
if (thumb) {
|
||||
char value1[128];
|
||||
char value2[128];
|
||||
char value3[128];
|
||||
if (IMB_metadata_get_field(
|
||||
thumb->metadata, "Thumb::Video::Width", value1, sizeof(value1)) &&
|
||||
IMB_metadata_get_field(
|
||||
thumb->metadata, "Thumb::Video::Height", value2, sizeof(value2)))
|
||||
{
|
||||
UI_tooltip_text_field_add(tip,
|
||||
BLI_sprintfN("%s \u00D7 %s", value1, value2),
|
||||
nullptr,
|
||||
UI_TIP_STYLE_NORMAL,
|
||||
UI_TIP_LC_NORMAL);
|
||||
}
|
||||
if (IMB_metadata_get_field(
|
||||
thumb->metadata, "Thumb::Video::Frames", value1, sizeof(value1)) &&
|
||||
IMB_metadata_get_field(thumb->metadata, "Thumb::Video::FPS", value2, sizeof(value2)) &&
|
||||
IMB_metadata_get_field(
|
||||
thumb->metadata, "Thumb::Video::Duration", value3, sizeof(value3)))
|
||||
{
|
||||
UI_tooltip_text_field_add(
|
||||
tip,
|
||||
BLI_sprintfN("%s %s @ %s %s", value1, N_("Frames"), value2, N_("FPS")),
|
||||
nullptr,
|
||||
UI_TIP_STYLE_NORMAL,
|
||||
UI_TIP_LC_NORMAL);
|
||||
UI_tooltip_text_field_add(tip,
|
||||
BLI_sprintfN("%s %s", value3, N_("seconds")),
|
||||
nullptr,
|
||||
UI_TIP_STYLE_NORMAL,
|
||||
UI_TIP_LC_NORMAL);
|
||||
UI_tooltip_text_field_add(tip, nullptr, nullptr, UI_TIP_STYLE_SPACER, UI_TIP_LC_NORMAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char date_st[FILELIST_DIRENTRY_DATE_LEN], time_st[FILELIST_DIRENTRY_TIME_LEN];
|
||||
bool is_today, is_yesterday;
|
||||
std::string day_string = ("");
|
||||
BLI_filelist_entry_datetime_to_string(
|
||||
NULL, file->time, false, time_st, date_st, &is_today, &is_yesterday);
|
||||
if (is_today || is_yesterday) {
|
||||
day_string = (is_today ? N_("Today") : N_("Yesterday")) + std::string(" ");
|
||||
}
|
||||
UI_tooltip_text_field_add(tip,
|
||||
BLI_sprintfN("%s: %s%s%s",
|
||||
N_("Modified"),
|
||||
day_string.c_str(),
|
||||
(is_today || is_yesterday) ? "" : date_st,
|
||||
(is_today || is_yesterday) ? time_st : ""),
|
||||
nullptr,
|
||||
UI_TIP_STYLE_NORMAL,
|
||||
UI_TIP_LC_NORMAL);
|
||||
|
||||
if (!(file->typeflag & FILE_TYPE_DIR) && file->size > 0) {
|
||||
char size[16];
|
||||
BLI_filelist_entry_size_to_string(NULL, file->size, false, size);
|
||||
if (file->size < 10000) {
|
||||
char size_full[16];
|
||||
BLI_str_format_uint64_grouped(size_full, file->size);
|
||||
UI_tooltip_text_field_add(
|
||||
tip,
|
||||
BLI_sprintfN("%s: %s (%s %s)", N_("Size"), size, size_full, N_("bytes")),
|
||||
nullptr,
|
||||
UI_TIP_STYLE_NORMAL,
|
||||
UI_TIP_LC_NORMAL);
|
||||
}
|
||||
else {
|
||||
UI_tooltip_text_field_add(tip,
|
||||
BLI_sprintfN("%s: %s", N_("Size"), size),
|
||||
nullptr,
|
||||
UI_TIP_STYLE_NORMAL,
|
||||
UI_TIP_LC_NORMAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (thumb && params->display != FILE_IMGDISPLAY) {
|
||||
float scale = (96.0f * UI_SCALE_FAC) / float(MAX2(thumb->x, thumb->y));
|
||||
short size[2] = {short(float(thumb->x) * scale), short(float(thumb->y) * scale)};
|
||||
UI_tooltip_text_field_add(tip, nullptr, nullptr, UI_TIP_STYLE_SPACER, UI_TIP_LC_NORMAL);
|
||||
UI_tooltip_text_field_add(tip, nullptr, nullptr, UI_TIP_STYLE_SPACER, UI_TIP_LC_NORMAL);
|
||||
UI_tooltip_image_field_add(tip, thumb, size);
|
||||
}
|
||||
|
||||
if (thumb && free_imbuf) {
|
||||
IMB_freeImBuf(thumb);
|
||||
}
|
||||
}
|
||||
|
||||
static char *file_draw_asset_tooltip_func(bContext * /*C*/, void *argN, const char * /*tip*/)
|
||||
|
@ -172,7 +394,7 @@ static void file_but_enable_drag(uiBut *but,
|
|||
|
||||
static uiBut *file_add_icon_but(const SpaceFile *sfile,
|
||||
uiBlock *block,
|
||||
const char *path,
|
||||
const char * /*path*/,
|
||||
const FileDirEntry *file,
|
||||
const rcti *tile_draw_rect,
|
||||
int icon,
|
||||
|
@ -195,7 +417,8 @@ static uiBut *file_add_icon_but(const SpaceFile *sfile,
|
|||
UI_but_func_tooltip_set(but, file_draw_asset_tooltip_func, file->asset, nullptr);
|
||||
}
|
||||
else {
|
||||
UI_but_func_tooltip_set(but, file_draw_tooltip_func, BLI_strdup(path), MEM_freeN);
|
||||
UI_but_func_tooltip_custom_set(
|
||||
but, file_draw_tooltip_custom_func, file_tooltip_data_create(sfile, file), MEM_freeN);
|
||||
}
|
||||
|
||||
return but;
|
||||
|
@ -335,7 +558,8 @@ static void file_add_preview_drag_but(const SpaceFile *sfile,
|
|||
UI_but_func_tooltip_set(but, file_draw_asset_tooltip_func, file->asset, nullptr);
|
||||
}
|
||||
else {
|
||||
UI_but_func_tooltip_set(but, file_draw_tooltip_func, BLI_strdup(path), MEM_freeN);
|
||||
UI_but_func_tooltip_custom_set(
|
||||
but, file_draw_tooltip_custom_func, file_tooltip_data_create(sfile, file), MEM_freeN);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1549,6 +1549,30 @@ ImBuf *IMB_anim_previewframe(anim *anim)
|
|||
IMB_freeImBuf(ibuf);
|
||||
position = anim->duration_in_frames / 2;
|
||||
ibuf = IMB_anim_absolute(anim, position, IMB_TC_NONE, IMB_PROXY_NONE);
|
||||
|
||||
char value[128];
|
||||
IMB_metadata_ensure(&ibuf->metadata);
|
||||
SNPRINTF(value, "%i", anim->x);
|
||||
IMB_metadata_set_field(ibuf->metadata, "Thumb::Video::Width", value);
|
||||
Harley marked this conversation as resolved
Outdated
Brecht Van Lommel
commented
If this metadata is specifically for thumbnails, it could use If this metadata is specifically for thumbnails, it could use `Thumb::Image` for all the fields?
|
||||
SNPRINTF(value, "%i", anim->y);
|
||||
IMB_metadata_set_field(ibuf->metadata, "Thumb::Video::Height", value);
|
||||
SNPRINTF(value, "%i", anim->duration_in_frames);
|
||||
IMB_metadata_set_field(ibuf->metadata, "Thumb::Video::Frames", value);
|
||||
|
||||
#ifdef WITH_FFMPEG
|
||||
if (anim->pFormatCtx && anim->curtype == ANIM_FFMPEG) {
|
||||
AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream];
|
||||
AVRational frame_rate = av_guess_frame_rate(anim->pFormatCtx, v_st, NULL);
|
||||
if (frame_rate.num != 0) {
|
||||
double duration = anim->duration_in_frames / av_q2d(frame_rate);
|
||||
SNPRINTF(value, "%g", av_q2d(frame_rate));
|
||||
IMB_metadata_set_field(ibuf->metadata, "Thumb::Video::FPS", value);
|
||||
SNPRINTF(value, "%g", duration);
|
||||
IMB_metadata_set_field(ibuf->metadata, "Thumb::Video::Duration", value);
|
||||
IMB_metadata_set_field(ibuf->metadata, "Thumb::Video::Codec", anim->pCodec->long_name);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return ibuf;
|
||||
}
|
||||
|
|
|
@ -92,6 +92,7 @@
|
|||
|
||||
#include "IMB_imbuf.h"
|
||||
#include "IMB_imbuf_types.h"
|
||||
#include "IMB_metadata.h"
|
||||
#include "IMB_thumbs.h"
|
||||
|
||||
#include "ED_asset.hh"
|
||||
|
@ -1722,6 +1723,12 @@ static ImBuf *blend_file_thumb_from_screenshot(bContext *C, BlendThumbnail **r_t
|
|||
/* File-system thumbnail image can be 256x256. */
|
||||
IMB_scaleImBuf(ibuf, ex * 2, ey * 2);
|
||||
|
||||
/* Save metadata for quick access. */
|
||||
char version_st[10] = {0};
|
||||
SNPRINTF(version_st, "%d.%01d", BLENDER_VERSION / 100, BLENDER_VERSION % 100);
|
||||
IMB_metadata_ensure(&ibuf->metadata);
|
||||
IMB_metadata_set_field(ibuf->metadata, "Thumb::Blender::Version", version_st);
|
||||
|
||||
/* Thumbnail inside blend should be 128x128. */
|
||||
ImBuf *thumb_ibuf = IMB_dupImBuf(ibuf);
|
||||
IMB_scaleImBuf(thumb_ibuf, ex, ey);
|
||||
|
@ -1831,6 +1838,13 @@ static ImBuf *blend_file_thumb_from_camera(const bContext *C,
|
|||
/* dirty oversampling */
|
||||
ImBuf *thumb_ibuf;
|
||||
thumb_ibuf = IMB_dupImBuf(ibuf);
|
||||
|
||||
/* Save metadata for quick access. */
|
||||
char version_st[10] = {0};
|
||||
SNPRINTF(version_st, "%d.%01d", BLENDER_VERSION / 100, BLENDER_VERSION % 100);
|
||||
IMB_metadata_ensure(&ibuf->metadata);
|
||||
IMB_metadata_set_field(ibuf->metadata, "Thumb::Blender::Version", version_st);
|
||||
|
||||
/* BLEN_THUMB_SIZE is size of thumbnail inside blend file: 128x128. */
|
||||
IMB_scaleImBuf(thumb_ibuf, BLEN_THUMB_SIZE, BLEN_THUMB_SIZE);
|
||||
thumb = BKE_main_thumbnail_from_imbuf(nullptr, thumb_ibuf);
|
||||
|
|
Loading…
Reference in New Issue