This was meant as an experiment to see how tangible it is to rewrite the File Browser UI code to be based on views, starting with the grid view for thumbnail mode. See T99890. My initial conclusion is that porting to views is quite doable, but we'll need some further UI code features to make certain things possible. Like big "composed" icons, where a file type icon is displayed on top of a big, generic file icon. There is a fair bit of stuff here that I'm not happy with. Plus things like selection, double clicking to open and renaming don't work yet. It's a start, a proof of concept even :)
1201 lines
40 KiB
C
1201 lines
40 KiB
C
/* SPDX-License-Identifier: GPL-2.0-or-later
|
|
* Copyright 2008 Blender Foundation. All rights reserved. */
|
|
|
|
/** \file
|
|
* \ingroup spfile
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <math.h>
|
|
#include <string.h>
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_alloca.h"
|
|
#include "BLI_blenlib.h"
|
|
#include "BLI_fileops_types.h"
|
|
#include "BLI_math.h"
|
|
#include "BLI_utildefines.h"
|
|
|
|
#ifdef WIN32
|
|
# include "BLI_winstuff.h"
|
|
#endif
|
|
|
|
#include "BIF_glutil.h"
|
|
|
|
#include "BKE_context.h"
|
|
#include "BKE_main.h"
|
|
|
|
#include "BLO_readfile.h"
|
|
|
|
#include "BLT_translation.h"
|
|
|
|
#include "BLF_api.h"
|
|
|
|
#include "IMB_imbuf_types.h"
|
|
|
|
#include "DNA_userdef_types.h"
|
|
#include "DNA_windowmanager_types.h"
|
|
|
|
#include "RNA_access.h"
|
|
#include "RNA_prototypes.h"
|
|
|
|
#include "ED_fileselect.h"
|
|
#include "ED_screen.h"
|
|
|
|
#include "UI_interface.h"
|
|
#include "UI_interface_icons.h"
|
|
#include "UI_resources.h"
|
|
#include "UI_view2d.h"
|
|
|
|
#include "WM_api.h"
|
|
#include "WM_types.h"
|
|
|
|
#include "GPU_immediate.h"
|
|
#include "GPU_immediate_util.h"
|
|
#include "GPU_state.h"
|
|
|
|
#include "filelist.h"
|
|
|
|
#include "file_intern.h" /* own include */
|
|
|
|
void ED_file_path_button(bScreen *screen,
|
|
const SpaceFile *sfile,
|
|
FileSelectParams *params,
|
|
uiBlock *block)
|
|
{
|
|
PointerRNA params_rna_ptr;
|
|
uiBut *but;
|
|
|
|
BLI_assert_msg(params != NULL,
|
|
"File select parameters not set. The caller is expected to check this.");
|
|
|
|
RNA_pointer_create(&screen->id, &RNA_FileSelectParams, params, ¶ms_rna_ptr);
|
|
|
|
/* callbacks for operator check functions */
|
|
UI_block_func_set(block, file_draw_check_cb, NULL, NULL);
|
|
|
|
but = uiDefButR(block,
|
|
UI_BTYPE_TEXT,
|
|
-1,
|
|
"",
|
|
0,
|
|
0,
|
|
UI_UNIT_X * 10,
|
|
UI_UNIT_Y,
|
|
¶ms_rna_ptr,
|
|
"directory",
|
|
0,
|
|
0.0f,
|
|
(float)FILE_MAX,
|
|
0.0f,
|
|
0.0f,
|
|
TIP_("File path"));
|
|
|
|
BLI_assert(!UI_but_flag_is_set(but, UI_BUT_UNDO));
|
|
BLI_assert(!UI_but_is_utf8(but));
|
|
|
|
UI_but_func_complete_set(but, autocomplete_directory, NULL);
|
|
UI_but_funcN_set(but, file_directory_enter_handle, NULL, but);
|
|
|
|
/* TODO: directory editing is non-functional while a library is loaded
|
|
* until this is properly supported just disable it. */
|
|
if (sfile && sfile->files && filelist_lib(sfile->files)) {
|
|
UI_but_flag_enable(but, UI_BUT_DISABLED);
|
|
}
|
|
|
|
/* clear func */
|
|
UI_block_func_set(block, NULL, NULL, NULL);
|
|
}
|
|
|
|
/* Dummy helper - we need dynamic tooltips here. */
|
|
static char *file_draw_tooltip_func(bContext *UNUSED(C), void *argN, const char *UNUSED(tip))
|
|
{
|
|
char *dyn_tooltip = argN;
|
|
return BLI_strdup(dyn_tooltip);
|
|
}
|
|
|
|
static void draw_tile_background(const rcti *draw_rect, int colorid, int shade)
|
|
{
|
|
float color[4];
|
|
rctf draw_rect_fl;
|
|
BLI_rctf_rcti_copy(&draw_rect_fl, draw_rect);
|
|
|
|
UI_GetThemeColorShade4fv(colorid, shade, color);
|
|
UI_draw_roundbox_corner_set(UI_CNR_ALL);
|
|
UI_draw_roundbox_aa(&draw_rect_fl, true, 5.0f, color);
|
|
}
|
|
|
|
static void file_draw_icon(const SpaceFile *sfile,
|
|
uiBlock *block,
|
|
const FileDirEntry *file,
|
|
const char *path,
|
|
const rcti *tile_draw_rect,
|
|
int icon,
|
|
int width,
|
|
int height,
|
|
bool drag,
|
|
bool dimmed)
|
|
{
|
|
uiBut *but;
|
|
|
|
const int x = tile_draw_rect->xmin;
|
|
const int y = tile_draw_rect->ymax - sfile->layout->tile_border_y - height;
|
|
|
|
/* For uiDefIconBut(), if a1==1.0 then a2 is alpha 0.0 - 1.0 */
|
|
const float a1 = dimmed ? 1.0f : 0.0f;
|
|
const float a2 = dimmed ? 0.3f : 0.0f;
|
|
but = uiDefIconBut(
|
|
block, UI_BTYPE_LABEL, 0, icon, x, y, width, height, NULL, 0.0f, 0.0f, a1, a2, NULL);
|
|
UI_but_func_tooltip_set(but, file_draw_tooltip_func, BLI_strdup(path), MEM_freeN);
|
|
|
|
if (drag) {
|
|
/* TODO: duplicated from file_draw_preview(). */
|
|
ID *id;
|
|
|
|
if ((id = filelist_file_get_id(file))) {
|
|
UI_but_drag_set_id(but, id);
|
|
ImBuf *preview_image = filelist_file_getimage(file);
|
|
if (preview_image) {
|
|
UI_but_drag_attach_image(but, preview_image, UI_DPI_FAC);
|
|
}
|
|
}
|
|
else if (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS &&
|
|
(file->typeflag & FILE_TYPE_ASSET) != 0) {
|
|
ImBuf *preview_image = filelist_file_getimage(file);
|
|
char blend_path[FILE_MAX_LIBEXTRA];
|
|
if (BLO_library_path_explode(path, blend_path, NULL, NULL)) {
|
|
const FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile);
|
|
BLI_assert(asset_params != NULL);
|
|
|
|
UI_but_drag_set_asset(but,
|
|
&(AssetHandle){.file_data = file},
|
|
BLI_strdup(blend_path),
|
|
file->asset_data,
|
|
asset_params->import_type,
|
|
icon,
|
|
preview_image,
|
|
UI_DPI_FAC);
|
|
}
|
|
}
|
|
else {
|
|
/* path is no more static, cannot give it directly to but... */
|
|
UI_but_drag_set_path(but, BLI_strdup(path), true);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void file_draw_string(int sx,
|
|
int sy,
|
|
const char *string,
|
|
float width,
|
|
int height,
|
|
eFontStyle_Align align,
|
|
const uchar col[4])
|
|
{
|
|
uiFontStyle fs;
|
|
rcti rect;
|
|
char fname[FILE_MAXFILE];
|
|
|
|
if (string[0] == '\0' || width < 1) {
|
|
return;
|
|
}
|
|
|
|
const uiStyle *style = UI_style_get();
|
|
fs = style->widget;
|
|
|
|
BLI_strncpy(fname, string, FILE_MAXFILE);
|
|
UI_text_clip_middle_ex(&fs, fname, width, UI_DPI_ICON_SIZE, sizeof(fname), '\0');
|
|
|
|
/* no text clipping needed, UI_fontstyle_draw does it but is a bit too strict
|
|
* (for buttons it works) */
|
|
rect.xmin = sx;
|
|
rect.xmax = sx + round_fl_to_int(width);
|
|
rect.ymin = sy - height;
|
|
rect.ymax = sy;
|
|
|
|
UI_fontstyle_draw(&fs,
|
|
&rect,
|
|
fname,
|
|
sizeof(fname),
|
|
col,
|
|
&(struct uiFontStyleDraw_Params){
|
|
.align = align,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* \param r_sx, r_sy: The lower right corner of the last line drawn, plus the height of the last
|
|
* line. This is the cursor position on completion to allow drawing more text
|
|
* behind that.
|
|
*/
|
|
static void file_draw_string_multiline(int sx,
|
|
int sy,
|
|
const char *string,
|
|
int wrap_width,
|
|
int line_height,
|
|
const uchar text_col[4],
|
|
int *r_sx,
|
|
int *r_sy)
|
|
{
|
|
rcti rect;
|
|
|
|
if (string[0] == '\0' || wrap_width < 1) {
|
|
return;
|
|
}
|
|
|
|
const uiStyle *style = UI_style_get();
|
|
int font_id = style->widget.uifont_id;
|
|
int len = strlen(string);
|
|
|
|
rcti textbox;
|
|
BLF_wordwrap(font_id, wrap_width);
|
|
BLF_enable(font_id, BLF_WORD_WRAP);
|
|
BLF_boundbox(font_id, string, len, &textbox);
|
|
BLF_disable(font_id, BLF_WORD_WRAP);
|
|
|
|
/* no text clipping needed, UI_fontstyle_draw does it but is a bit too strict
|
|
* (for buttons it works) */
|
|
rect.xmin = sx;
|
|
rect.xmax = sx + wrap_width;
|
|
/* Need to increase the clipping rect by one more line, since the #UI_fontstyle_draw_ex() will
|
|
* actually start drawing at (ymax - line-height). */
|
|
rect.ymin = sy - BLI_rcti_size_y(&textbox) - line_height;
|
|
rect.ymax = sy;
|
|
|
|
struct ResultBLF result;
|
|
UI_fontstyle_draw_ex(&style->widget,
|
|
&rect,
|
|
string,
|
|
len,
|
|
text_col,
|
|
&(struct uiFontStyleDraw_Params){
|
|
.align = UI_STYLE_TEXT_LEFT,
|
|
.word_wrap = true,
|
|
},
|
|
NULL,
|
|
NULL,
|
|
&result);
|
|
if (r_sx) {
|
|
*r_sx = result.width;
|
|
}
|
|
if (r_sy) {
|
|
*r_sy = rect.ymin + line_height;
|
|
}
|
|
}
|
|
|
|
void file_calc_previews(const bContext *C, ARegion *region)
|
|
{
|
|
SpaceFile *sfile = CTX_wm_space_file(C);
|
|
View2D *v2d = ®ion->v2d;
|
|
|
|
ED_fileselect_init_layout(sfile, region);
|
|
UI_view2d_totRect_set(v2d, sfile->layout->width, sfile->layout->height);
|
|
}
|
|
|
|
static void file_draw_preview(const SpaceFile *sfile,
|
|
uiBlock *block,
|
|
const FileDirEntry *file,
|
|
const char *path,
|
|
const rcti *tile_draw_rect,
|
|
const float icon_aspect,
|
|
ImBuf *imb,
|
|
const int icon,
|
|
FileLayout *layout,
|
|
const bool is_icon,
|
|
const bool drag,
|
|
const bool dimmed,
|
|
const bool is_link)
|
|
{
|
|
uiBut *but;
|
|
float fx, fy;
|
|
float dx, dy;
|
|
int xco, yco;
|
|
float ui_imbx, ui_imby;
|
|
float scaledx, scaledy;
|
|
float scale;
|
|
int ex, ey;
|
|
bool show_outline = !is_icon &&
|
|
(file->typeflag & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_BLENDER));
|
|
const bool is_offline = (file->attributes & FILE_ATTR_OFFLINE);
|
|
|
|
BLI_assert(imb != NULL);
|
|
|
|
ui_imbx = imb->x * UI_DPI_FAC;
|
|
ui_imby = imb->y * UI_DPI_FAC;
|
|
/* Unlike thumbnails, icons are not scaled up. */
|
|
if (((ui_imbx > layout->prv_w) || (ui_imby > layout->prv_h)) ||
|
|
(!is_icon && ((ui_imbx < layout->prv_w) || (ui_imby < layout->prv_h)))) {
|
|
if (imb->x > imb->y) {
|
|
scaledx = (float)layout->prv_w;
|
|
scaledy = ((float)imb->y / (float)imb->x) * layout->prv_w;
|
|
scale = scaledx / imb->x;
|
|
}
|
|
else {
|
|
scaledy = (float)layout->prv_h;
|
|
scaledx = ((float)imb->x / (float)imb->y) * layout->prv_h;
|
|
scale = scaledy / imb->y;
|
|
}
|
|
}
|
|
else {
|
|
scaledx = ui_imbx;
|
|
scaledy = ui_imby;
|
|
scale = UI_DPI_FAC;
|
|
}
|
|
|
|
ex = (int)scaledx;
|
|
ey = (int)scaledy;
|
|
fx = ((float)layout->prv_w - (float)ex) / 2.0f;
|
|
fy = ((float)layout->prv_h - (float)ey) / 2.0f;
|
|
dx = (fx + 0.5f + layout->prv_border_x);
|
|
dy = (fy + 0.5f - layout->prv_border_y);
|
|
xco = tile_draw_rect->xmin + (int)dx;
|
|
yco = tile_draw_rect->ymax - layout->prv_h + (int)dy;
|
|
|
|
GPU_blend(GPU_BLEND_ALPHA);
|
|
|
|
/* the large image */
|
|
|
|
float col[4] = {1.0f, 1.0f, 1.0f, 1.0f};
|
|
if (is_icon) {
|
|
if (file->typeflag & FILE_TYPE_DIR) {
|
|
UI_GetThemeColor4fv(TH_ICON_FOLDER, col);
|
|
}
|
|
else {
|
|
UI_GetThemeColor4fv(TH_TEXT, col);
|
|
}
|
|
}
|
|
else if (file->typeflag & FILE_TYPE_FTFONT) {
|
|
UI_GetThemeColor4fv(TH_TEXT, col);
|
|
}
|
|
|
|
if (dimmed) {
|
|
col[3] *= 0.3f;
|
|
}
|
|
|
|
if (!is_icon && file->typeflag & FILE_TYPE_BLENDERLIB) {
|
|
/* Datablock preview images use premultiplied alpha. */
|
|
GPU_blend(GPU_BLEND_ALPHA_PREMULT);
|
|
}
|
|
|
|
IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR);
|
|
immDrawPixelsTexTiled_scaling(&state,
|
|
(float)xco,
|
|
(float)yco,
|
|
imb->x,
|
|
imb->y,
|
|
GPU_RGBA8,
|
|
true,
|
|
imb->rect,
|
|
scale,
|
|
scale,
|
|
1.0f,
|
|
1.0f,
|
|
col);
|
|
|
|
GPU_blend(GPU_BLEND_ALPHA);
|
|
|
|
if (icon && is_icon) {
|
|
/* Small icon in the middle of large image, scaled to fit container and UI scale */
|
|
float icon_x, icon_y;
|
|
const float icon_size = 16.0f / icon_aspect * U.dpi_fac;
|
|
float icon_opacity = 0.3f;
|
|
uchar icon_color[4] = {0, 0, 0, 255};
|
|
float bgcolor[4];
|
|
UI_GetThemeColor4fv(TH_ICON_FOLDER, bgcolor);
|
|
if (rgb_to_grayscale(bgcolor) < 0.5f) {
|
|
icon_color[0] = 255;
|
|
icon_color[1] = 255;
|
|
icon_color[2] = 255;
|
|
}
|
|
icon_x = xco + (ex / 2.0f) - (icon_size / 2.0f);
|
|
icon_y = yco + (ey / 2.0f) - (icon_size * ((file->typeflag & FILE_TYPE_DIR) ? 0.78f : 0.75f));
|
|
UI_icon_draw_ex(
|
|
icon_x, icon_y, icon, icon_aspect / U.dpi_fac, icon_opacity, 0.0f, icon_color, false);
|
|
}
|
|
|
|
if (is_link || is_offline) {
|
|
/* Icon at bottom to indicate it is a shortcut, link, alias, or offline. */
|
|
float icon_x, icon_y;
|
|
icon_x = xco + (2.0f * UI_DPI_FAC);
|
|
icon_y = yco + (2.0f * UI_DPI_FAC);
|
|
const int arrow = is_link ? ICON_LOOP_FORWARDS : ICON_URL;
|
|
if (!is_icon) {
|
|
/* At very bottom-left if preview style. */
|
|
const uchar dark[4] = {0, 0, 0, 255};
|
|
const uchar light[4] = {255, 255, 255, 255};
|
|
UI_icon_draw_ex(icon_x + 1, icon_y - 1, arrow, 1.0f / U.dpi_fac, 0.2f, 0.0f, dark, false);
|
|
UI_icon_draw_ex(icon_x, icon_y, arrow, 1.0f / U.dpi_fac, 0.6f, 0.0f, light, false);
|
|
}
|
|
else {
|
|
/* Link to folder or non-previewed file. */
|
|
uchar icon_color[4];
|
|
UI_GetThemeColor4ubv(TH_BACK, icon_color);
|
|
icon_x = xco + ((file->typeflag & FILE_TYPE_DIR) ? 0.14f : 0.23f) * scaledx;
|
|
icon_y = yco + ((file->typeflag & FILE_TYPE_DIR) ? 0.24f : 0.14f) * scaledy;
|
|
UI_icon_draw_ex(
|
|
icon_x, icon_y, arrow, icon_aspect / U.dpi_fac * 1.8, 0.3f, 0.0f, icon_color, false);
|
|
}
|
|
}
|
|
else if (icon && !is_icon && !(file->typeflag & FILE_TYPE_FTFONT)) {
|
|
/* Smaller, fainter icon at bottom-left for preview image thumbnail, but not for fonts. */
|
|
float icon_x, icon_y;
|
|
const uchar dark[4] = {0, 0, 0, 255};
|
|
const uchar light[4] = {255, 255, 255, 255};
|
|
icon_x = xco + (2.0f * UI_DPI_FAC);
|
|
icon_y = yco + (2.0f * UI_DPI_FAC);
|
|
UI_icon_draw_ex(icon_x + 1, icon_y - 1, icon, 1.0f / U.dpi_fac, 0.2f, 0.0f, dark, false);
|
|
UI_icon_draw_ex(icon_x, icon_y, icon, 1.0f / U.dpi_fac, 0.6f, 0.0f, light, false);
|
|
}
|
|
|
|
const bool is_current_main_data = filelist_file_get_id(file) != NULL;
|
|
if (is_current_main_data) {
|
|
/* Smaller, fainter icon at the top-right indicating that the file represents data from the
|
|
* current file (from current #Main in fact). */
|
|
float icon_x, icon_y;
|
|
const uchar light[4] = {255, 255, 255, 255};
|
|
icon_x = xco + ex - UI_UNIT_X;
|
|
icon_y = yco + ey - UI_UNIT_Y;
|
|
UI_icon_draw_ex(icon_x, icon_y, ICON_CURRENT_FILE, 1.0f / U.dpi_fac, 0.6f, 0.0f, light, false);
|
|
}
|
|
|
|
/* Contrasting outline around some preview types. */
|
|
if (show_outline) {
|
|
GPUVertFormat *format = immVertexFormat();
|
|
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
|
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
|
|
float border_color[4] = {1.0f, 1.0f, 1.0f, 0.4f};
|
|
float bgcolor[4];
|
|
UI_GetThemeColor4fv(TH_BACK, bgcolor);
|
|
if (rgb_to_grayscale(bgcolor) > 0.5f) {
|
|
border_color[0] = 0.0f;
|
|
border_color[1] = 0.0f;
|
|
border_color[2] = 0.0f;
|
|
}
|
|
immUniformColor4fv(border_color);
|
|
imm_draw_box_wire_2d(pos, (float)xco, (float)yco, (float)(xco + ex), (float)(yco + ey));
|
|
immUnbindProgram();
|
|
}
|
|
|
|
/* Invisible button for dragging. */
|
|
rcti drag_rect = *tile_draw_rect;
|
|
/* A bit smaller than the full tile, to increase the gap between items that users can drag from
|
|
* for box select. */
|
|
BLI_rcti_pad(&drag_rect, -layout->tile_border_x, -layout->tile_border_y);
|
|
|
|
but = uiDefBut(block,
|
|
UI_BTYPE_LABEL,
|
|
0,
|
|
"",
|
|
drag_rect.xmin,
|
|
drag_rect.ymin,
|
|
BLI_rcti_size_x(&drag_rect),
|
|
BLI_rcti_size_y(&drag_rect),
|
|
NULL,
|
|
0.0,
|
|
0.0,
|
|
0,
|
|
0,
|
|
NULL);
|
|
|
|
/* Drag-region. */
|
|
if (drag) {
|
|
ID *id;
|
|
|
|
if ((id = filelist_file_get_id(file))) {
|
|
UI_but_drag_set_id(but, id);
|
|
UI_but_drag_attach_image(but, imb, scale);
|
|
}
|
|
/* path is no more static, cannot give it directly to but... */
|
|
else if (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS &&
|
|
(file->typeflag & FILE_TYPE_ASSET) != 0) {
|
|
char blend_path[FILE_MAX_LIBEXTRA];
|
|
|
|
if (BLO_library_path_explode(path, blend_path, NULL, NULL)) {
|
|
const FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile);
|
|
BLI_assert(asset_params != NULL);
|
|
|
|
UI_but_drag_set_asset(but,
|
|
&(AssetHandle){.file_data = file},
|
|
BLI_strdup(blend_path),
|
|
file->asset_data,
|
|
asset_params->import_type,
|
|
icon,
|
|
imb,
|
|
scale);
|
|
}
|
|
}
|
|
else {
|
|
UI_but_drag_set_image(but, BLI_strdup(path), icon, imb, scale, true);
|
|
}
|
|
}
|
|
|
|
GPU_blend(GPU_BLEND_NONE);
|
|
}
|
|
|
|
static void renamebutton_cb(bContext *C, void *UNUSED(arg1), char *oldname)
|
|
{
|
|
char newname[FILE_MAX + 12];
|
|
char orgname[FILE_MAX + 12];
|
|
char filename[FILE_MAX + 12];
|
|
wmWindowManager *wm = CTX_wm_manager(C);
|
|
wmWindow *win = CTX_wm_window(C);
|
|
SpaceFile *sfile = (SpaceFile *)CTX_wm_space_data(C);
|
|
ARegion *region = CTX_wm_region(C);
|
|
FileSelectParams *params = ED_fileselect_get_active_params(sfile);
|
|
|
|
BLI_join_dirfile(orgname, sizeof(orgname), params->dir, oldname);
|
|
BLI_strncpy(filename, params->renamefile, sizeof(filename));
|
|
BLI_filename_make_safe(filename);
|
|
BLI_join_dirfile(newname, sizeof(newname), params->dir, filename);
|
|
|
|
if (!STREQ(orgname, newname)) {
|
|
if (!BLI_exists(newname)) {
|
|
errno = 0;
|
|
if ((BLI_rename(orgname, newname) != 0) || !BLI_exists(newname)) {
|
|
WM_reportf(RPT_ERROR, "Could not rename: %s", errno ? strerror(errno) : "unknown error");
|
|
WM_report_banner_show();
|
|
}
|
|
else {
|
|
/* If rename is successful, scroll to newly renamed entry. */
|
|
BLI_strncpy(params->renamefile, filename, sizeof(params->renamefile));
|
|
file_params_invoke_rename_postscroll(wm, win, sfile);
|
|
}
|
|
|
|
/* to make sure we show what is on disk */
|
|
ED_fileselect_clear(wm, sfile);
|
|
}
|
|
else {
|
|
/* Renaming failed, reset the name for further renaming handling. */
|
|
BLI_strncpy(params->renamefile, oldname, sizeof(params->renamefile));
|
|
}
|
|
|
|
ED_region_tag_redraw(region);
|
|
}
|
|
}
|
|
|
|
static void draw_background(FileLayout *layout, View2D *v2d)
|
|
{
|
|
const int item_height = layout->tile_h + (2 * layout->tile_border_y);
|
|
int i;
|
|
int sy;
|
|
|
|
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
|
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
|
|
float col_alternating[4];
|
|
UI_GetThemeColor4fv(TH_ROW_ALTERNATE, col_alternating);
|
|
immUniformThemeColorBlend(TH_BACK, TH_ROW_ALTERNATE, col_alternating[3]);
|
|
|
|
/* alternating flat shade background */
|
|
for (i = 2; (i <= layout->rows + 1); i += 2) {
|
|
sy = (int)v2d->cur.ymax - layout->offset_top - i * item_height - layout->tile_border_y;
|
|
|
|
/* Offset pattern slightly to add scroll effect. */
|
|
sy += round_fl_to_int(item_height * (v2d->tot.ymax - v2d->cur.ymax) / item_height);
|
|
|
|
immRectf(pos,
|
|
v2d->cur.xmin,
|
|
(float)sy,
|
|
v2d->cur.xmax,
|
|
(float)(sy + layout->tile_h + 2 * layout->tile_border_y));
|
|
}
|
|
|
|
immUnbindProgram();
|
|
}
|
|
|
|
static void draw_dividers(FileLayout *layout, View2D *v2d)
|
|
{
|
|
/* vertical column dividers */
|
|
|
|
const int step = (layout->tile_w + 2 * layout->tile_border_x);
|
|
|
|
uint vertex_len = 0;
|
|
int sx = (int)v2d->tot.xmin;
|
|
while (sx < v2d->cur.xmax) {
|
|
sx += step;
|
|
vertex_len += 4; /* vertex_count = 2 points per line * 2 lines per divider */
|
|
}
|
|
|
|
if (vertex_len > 0) {
|
|
int v1[2], v2[2];
|
|
uchar col_hi[3], col_lo[3];
|
|
|
|
UI_GetThemeColorShade3ubv(TH_BACK, 30, col_hi);
|
|
UI_GetThemeColorShade3ubv(TH_BACK, -30, col_lo);
|
|
|
|
v1[1] = v2d->cur.ymax - layout->tile_border_y;
|
|
v2[1] = v2d->cur.ymin;
|
|
|
|
GPUVertFormat *format = immVertexFormat();
|
|
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
|
|
uint color = GPU_vertformat_attr_add(
|
|
format, "color", GPU_COMP_U8, 3, GPU_FETCH_INT_TO_FLOAT_UNIT);
|
|
|
|
immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
|
|
immBegin(GPU_PRIM_LINES, vertex_len);
|
|
|
|
sx = (int)v2d->tot.xmin;
|
|
while (sx < v2d->cur.xmax) {
|
|
sx += step;
|
|
|
|
v1[0] = v2[0] = sx;
|
|
immAttrSkip(color);
|
|
immVertex2iv(pos, v1);
|
|
immAttr3ubv(color, col_lo);
|
|
immVertex2iv(pos, v2);
|
|
|
|
v1[0] = v2[0] = sx + 1;
|
|
immAttrSkip(color);
|
|
immVertex2iv(pos, v1);
|
|
immAttr3ubv(color, col_hi);
|
|
immVertex2iv(pos, v2);
|
|
}
|
|
|
|
immEnd();
|
|
immUnbindProgram();
|
|
}
|
|
}
|
|
|
|
static void draw_columnheader_background(const FileLayout *layout, const View2D *v2d)
|
|
{
|
|
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
|
|
|
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
|
|
immUniformThemeColorShade(TH_BACK, 11);
|
|
|
|
immRectf(pos,
|
|
v2d->cur.xmin,
|
|
v2d->cur.ymax - layout->attribute_column_header_h,
|
|
v2d->cur.xmax,
|
|
v2d->cur.ymax);
|
|
|
|
immUnbindProgram();
|
|
}
|
|
|
|
static void draw_columnheader_columns(const FileSelectParams *params,
|
|
FileLayout *layout,
|
|
const View2D *v2d,
|
|
const uchar text_col[4])
|
|
{
|
|
const float divider_pad = 0.2 * layout->attribute_column_header_h;
|
|
int sx = v2d->cur.xmin, sy = v2d->cur.ymax;
|
|
|
|
for (FileAttributeColumnType column_type = 0; column_type < ATTRIBUTE_COLUMN_MAX;
|
|
column_type++) {
|
|
if (!file_attribute_column_type_enabled(params, column_type)) {
|
|
continue;
|
|
}
|
|
const FileAttributeColumn *column = &layout->attribute_columns[column_type];
|
|
|
|
/* Active sort type triangle */
|
|
if (params->sort == column->sort_type) {
|
|
float tri_color[4];
|
|
|
|
rgba_uchar_to_float(tri_color, text_col);
|
|
UI_draw_icon_tri(sx + column->width - (0.3f * U.widget_unit) -
|
|
ATTRIBUTE_COLUMN_PADDING / 2.0f,
|
|
sy + (0.1f * U.widget_unit) - (layout->attribute_column_header_h / 2),
|
|
(params->flag & FILE_SORT_INVERT) ? 't' : 'v',
|
|
tri_color);
|
|
}
|
|
|
|
file_draw_string(sx + ATTRIBUTE_COLUMN_PADDING,
|
|
sy - layout->tile_border_y,
|
|
IFACE_(column->name),
|
|
column->width - 2 * ATTRIBUTE_COLUMN_PADDING,
|
|
layout->attribute_column_header_h - layout->tile_border_y,
|
|
UI_STYLE_TEXT_LEFT,
|
|
text_col);
|
|
|
|
/* Separator line */
|
|
if (column_type != COLUMN_NAME) {
|
|
uint pos = GPU_vertformat_attr_add(
|
|
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
|
|
|
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
|
|
immUniformThemeColorShade(TH_BACK, -10);
|
|
immBegin(GPU_PRIM_LINES, 2);
|
|
immVertex2f(pos, sx - 1, sy - divider_pad);
|
|
immVertex2f(pos, sx - 1, sy - layout->attribute_column_header_h + divider_pad);
|
|
immEnd();
|
|
immUnbindProgram();
|
|
}
|
|
|
|
sx += column->width;
|
|
}
|
|
|
|
/* Vertical separator lines line */
|
|
{
|
|
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
|
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
|
|
immUniformThemeColorShade(TH_BACK, -10);
|
|
immBegin(GPU_PRIM_LINES, 4);
|
|
immVertex2f(pos, v2d->cur.xmin, sy);
|
|
immVertex2f(pos, v2d->cur.xmax, sy);
|
|
immVertex2f(pos, v2d->cur.xmin, sy - layout->attribute_column_header_h);
|
|
immVertex2f(pos, v2d->cur.xmax, sy - layout->attribute_column_header_h);
|
|
immEnd();
|
|
immUnbindProgram();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates the stat string stored in file->entry if necessary.
|
|
*/
|
|
static const char *filelist_get_details_column_string(
|
|
FileAttributeColumnType column,
|
|
/* Generated string will be cached in the file, so non-const. */
|
|
FileDirEntry *file,
|
|
const bool small_size,
|
|
const bool update_stat_strings)
|
|
{
|
|
switch (column) {
|
|
case COLUMN_DATETIME:
|
|
if (!(file->typeflag & FILE_TYPE_BLENDERLIB) && !FILENAME_IS_CURRPAR(file->relpath)) {
|
|
if ((file->draw_data.datetime_str[0] == '\0') || update_stat_strings) {
|
|
char date[FILELIST_DIRENTRY_DATE_LEN], time[FILELIST_DIRENTRY_TIME_LEN];
|
|
bool is_today, is_yesterday;
|
|
|
|
BLI_filelist_entry_datetime_to_string(
|
|
NULL, file->time, small_size, time, date, &is_today, &is_yesterday);
|
|
|
|
if (is_today || is_yesterday) {
|
|
BLI_strncpy(date, is_today ? N_("Today") : N_("Yesterday"), sizeof(date));
|
|
}
|
|
BLI_snprintf(file->draw_data.datetime_str,
|
|
sizeof(file->draw_data.datetime_str),
|
|
"%s %s",
|
|
date,
|
|
time);
|
|
}
|
|
|
|
return file->draw_data.datetime_str;
|
|
}
|
|
break;
|
|
case COLUMN_SIZE:
|
|
if ((file->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) ||
|
|
!(file->typeflag & (FILE_TYPE_DIR | FILE_TYPE_BLENDERLIB))) {
|
|
if ((file->draw_data.size_str[0] == '\0') || update_stat_strings) {
|
|
BLI_filelist_entry_size_to_string(
|
|
NULL, file->size, small_size, file->draw_data.size_str);
|
|
}
|
|
|
|
return file->draw_data.size_str;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void draw_details_columns(const FileSelectParams *params,
|
|
const FileLayout *layout,
|
|
FileDirEntry *file,
|
|
const rcti *tile_draw_rect,
|
|
const uchar text_col[4])
|
|
{
|
|
const bool small_size = SMALL_SIZE_CHECK(params->thumbnail_size);
|
|
const bool update_stat_strings = small_size != SMALL_SIZE_CHECK(layout->curr_size);
|
|
int sx = tile_draw_rect->xmin - layout->tile_border_x - (UI_UNIT_X * 0.1f);
|
|
|
|
for (FileAttributeColumnType column_type = 0; column_type < ATTRIBUTE_COLUMN_MAX;
|
|
column_type++) {
|
|
const FileAttributeColumn *column = &layout->attribute_columns[column_type];
|
|
|
|
/* Name column is not a detail column (should already be drawn), always skip here. */
|
|
if (column_type == COLUMN_NAME) {
|
|
sx += column->width;
|
|
continue;
|
|
}
|
|
if (!file_attribute_column_type_enabled(params, column_type)) {
|
|
continue;
|
|
}
|
|
|
|
const char *str = filelist_get_details_column_string(
|
|
column_type, file, small_size, update_stat_strings);
|
|
|
|
if (str) {
|
|
file_draw_string(sx + ATTRIBUTE_COLUMN_PADDING,
|
|
tile_draw_rect->ymax - layout->tile_border_y,
|
|
IFACE_(str),
|
|
column->width - 2 * ATTRIBUTE_COLUMN_PADDING,
|
|
layout->tile_h,
|
|
column->text_align,
|
|
text_col);
|
|
}
|
|
|
|
sx += column->width;
|
|
}
|
|
}
|
|
|
|
static rcti tile_draw_rect_get(const View2D *v2d,
|
|
const FileLayout *layout,
|
|
const enum eFileDisplayType display,
|
|
const int file_idx,
|
|
const int padx)
|
|
{
|
|
int tile_pos_x, tile_pos_y;
|
|
ED_fileselect_layout_tilepos(layout, file_idx, &tile_pos_x, &tile_pos_y);
|
|
tile_pos_x += (int)(v2d->tot.xmin);
|
|
tile_pos_y = (int)(v2d->tot.ymax - tile_pos_y);
|
|
|
|
rcti rect;
|
|
rect.xmin = tile_pos_x + padx;
|
|
rect.xmax = rect.xmin + (ELEM(display, FILE_VERTICALDISPLAY, FILE_HORIZONTALDISPLAY) ?
|
|
layout->tile_w - (2 * padx) :
|
|
layout->tile_w);
|
|
rect.ymax = tile_pos_y;
|
|
rect.ymin = rect.ymax - layout->tile_h - layout->tile_border_y;
|
|
|
|
return rect;
|
|
}
|
|
|
|
void file_draw_list(const bContext *C, ARegion *region)
|
|
{
|
|
wmWindowManager *wm = CTX_wm_manager(C);
|
|
wmWindow *win = CTX_wm_window(C);
|
|
SpaceFile *sfile = CTX_wm_space_file(C);
|
|
FileSelectParams *params = ED_fileselect_get_active_params(sfile);
|
|
FileLayout *layout = ED_fileselect_get_layout(sfile, region);
|
|
View2D *v2d = ®ion->v2d;
|
|
struct FileList *files = sfile->files;
|
|
struct FileDirEntry *file;
|
|
const char *root = filelist_dir(files);
|
|
ImBuf *imb;
|
|
uiBlock *block = UI_block_begin(C, region, __func__, UI_EMBOSS);
|
|
int numfiles;
|
|
int numfiles_layout;
|
|
int offset;
|
|
int textwidth, textheight;
|
|
int i;
|
|
bool is_icon;
|
|
eFontStyle_Align align;
|
|
bool do_drag;
|
|
uchar text_col[4];
|
|
const bool draw_columnheader = (params->display == FILE_VERTICALDISPLAY);
|
|
const float thumb_icon_aspect = MIN2(64.0f / (float)(params->thumbnail_size), 1.0f);
|
|
|
|
numfiles = filelist_files_ensure(files);
|
|
|
|
if (params->display != FILE_IMGDISPLAY) {
|
|
draw_background(layout, v2d);
|
|
draw_dividers(layout, v2d);
|
|
}
|
|
|
|
offset = ED_fileselect_layout_offset(
|
|
layout, (int)region->v2d.cur.xmin, (int)-region->v2d.cur.ymax);
|
|
if (offset < 0) {
|
|
offset = 0;
|
|
}
|
|
|
|
numfiles_layout = ED_fileselect_layout_numfiles(layout, region);
|
|
|
|
/* adjust, so the next row is already drawn when scrolling */
|
|
if (layout->flag & FILE_LAYOUT_HOR) {
|
|
numfiles_layout += layout->rows;
|
|
}
|
|
else {
|
|
numfiles_layout += layout->flow_columns;
|
|
}
|
|
|
|
filelist_file_cache_slidingwindow_set(files, numfiles_layout);
|
|
|
|
textwidth = (FILE_IMGDISPLAY == params->display) ?
|
|
layout->tile_w :
|
|
round_fl_to_int(layout->attribute_columns[COLUMN_NAME].width);
|
|
textheight = (int)(layout->textheight * 3.0 / 2.0 + 0.5);
|
|
|
|
align = (FILE_IMGDISPLAY == params->display) ? UI_STYLE_TEXT_CENTER : UI_STYLE_TEXT_LEFT;
|
|
|
|
if (numfiles > 0) {
|
|
const bool success = filelist_file_cache_block(
|
|
files, min_ii(offset + (numfiles_layout / 2), numfiles - 1));
|
|
BLI_assert(success);
|
|
UNUSED_VARS_NDEBUG(success);
|
|
|
|
filelist_cache_previews_update(files);
|
|
|
|
/* Handle preview timer here,
|
|
* since it's filelist_file_cache_block() and filelist_cache_previews_update()
|
|
* which controls previews task. */
|
|
{
|
|
const bool previews_running = filelist_cache_previews_running(files) &&
|
|
!filelist_cache_previews_done(files);
|
|
// printf("%s: preview task: %d\n", __func__, previews_running);
|
|
if (previews_running && !sfile->previews_timer) {
|
|
sfile->previews_timer = WM_event_add_timer_notifier(
|
|
wm, win, NC_SPACE | ND_SPACE_FILE_PREVIEW, 0.01);
|
|
}
|
|
if (!previews_running && sfile->previews_timer) {
|
|
/* Preview is not running, no need to keep generating update events! */
|
|
// printf("%s: Inactive preview task, sleeping!\n", __func__);
|
|
WM_event_remove_timer_notifier(wm, win, sfile->previews_timer);
|
|
sfile->previews_timer = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
BLF_batch_draw_begin();
|
|
|
|
UI_GetThemeColor4ubv(TH_TEXT, text_col);
|
|
|
|
for (i = offset; (i < numfiles) && (i < offset + numfiles_layout); i++) {
|
|
uint file_selflag;
|
|
char path[FILE_MAX_LIBEXTRA];
|
|
const int padx = 0.1f * UI_UNIT_X;
|
|
int icon_ofs = 0;
|
|
|
|
const rcti tile_draw_rect = tile_draw_rect_get(v2d, layout, params->display, i, padx);
|
|
|
|
file = filelist_file(files, i);
|
|
file_selflag = filelist_entry_select_get(sfile->files, file, CHECK_ALL);
|
|
|
|
BLI_join_dirfile(path, sizeof(path), root, file->relpath);
|
|
|
|
if (!(file_selflag & FILE_SEL_EDITING)) {
|
|
if ((params->highlight_file == i) || (file_selflag & FILE_SEL_HIGHLIGHTED) ||
|
|
(file_selflag & FILE_SEL_SELECTED)) {
|
|
int colorid = (file_selflag & FILE_SEL_SELECTED) ? TH_HILITE : TH_BACK;
|
|
int shade = (params->highlight_file == i) || (file_selflag & FILE_SEL_HIGHLIGHTED) ? 35 :
|
|
0;
|
|
BLI_assert(i == 0 || !FILENAME_IS_CURRPAR(file->relpath));
|
|
|
|
rcti tile_bg_rect = tile_draw_rect;
|
|
/* One pixel downwards, places it more in the center. */
|
|
BLI_rcti_translate(&tile_bg_rect, 0, -1);
|
|
draw_tile_background(&tile_bg_rect, colorid, shade);
|
|
}
|
|
}
|
|
UI_draw_roundbox_corner_set(UI_CNR_NONE);
|
|
|
|
/* don't drag parent or refresh items */
|
|
do_drag = !(FILENAME_IS_CURRPAR(file->relpath));
|
|
const bool is_hidden = (file->attributes & FILE_ATTR_HIDDEN);
|
|
const bool is_link = (file->attributes & FILE_ATTR_ANY_LINK);
|
|
|
|
if (FILE_IMGDISPLAY == params->display) {
|
|
const int icon = filelist_geticon(files, i, false);
|
|
is_icon = 0;
|
|
imb = filelist_getimage(files, i);
|
|
if (!imb) {
|
|
imb = filelist_geticon_image(files, i);
|
|
is_icon = 1;
|
|
}
|
|
|
|
file_draw_preview(sfile,
|
|
block,
|
|
file,
|
|
path,
|
|
&tile_draw_rect,
|
|
thumb_icon_aspect,
|
|
imb,
|
|
icon,
|
|
layout,
|
|
is_icon,
|
|
do_drag,
|
|
is_hidden,
|
|
is_link);
|
|
}
|
|
else {
|
|
file_draw_icon(sfile,
|
|
block,
|
|
file,
|
|
path,
|
|
&tile_draw_rect,
|
|
filelist_geticon(files, i, true),
|
|
ICON_DEFAULT_WIDTH_SCALE,
|
|
ICON_DEFAULT_HEIGHT_SCALE,
|
|
do_drag,
|
|
is_hidden);
|
|
icon_ofs += ICON_DEFAULT_WIDTH_SCALE + 0.2f * UI_UNIT_X;
|
|
}
|
|
|
|
if (file_selflag & FILE_SEL_EDITING) {
|
|
const short width = (params->display == FILE_IMGDISPLAY) ?
|
|
textwidth :
|
|
layout->attribute_columns[COLUMN_NAME].width -
|
|
ATTRIBUTE_COLUMN_PADDING;
|
|
|
|
uiBut *but = uiDefBut(block,
|
|
UI_BTYPE_TEXT,
|
|
1,
|
|
"",
|
|
tile_draw_rect.xmin + icon_ofs,
|
|
tile_draw_rect.ymin + layout->tile_border_y - 0.15f * UI_UNIT_X,
|
|
width - icon_ofs,
|
|
textheight,
|
|
params->renamefile,
|
|
1.0f,
|
|
(float)sizeof(params->renamefile),
|
|
0,
|
|
0,
|
|
"");
|
|
UI_but_func_rename_set(but, renamebutton_cb, file);
|
|
UI_but_flag_enable(but, UI_BUT_NO_UTF8); /* allow non utf8 names */
|
|
UI_but_flag_disable(but, UI_BUT_UNDO);
|
|
if (false == UI_but_active_only(C, region, block, but)) {
|
|
/* Note that this is the only place where we can also handle a cancelled renaming. */
|
|
|
|
file_params_rename_end(wm, win, sfile, file);
|
|
|
|
/* After the rename button is removed, we need to make sure the view is redrawn once more,
|
|
* in case selection changed. Usually UI code would trigger that redraw, but the rename
|
|
* operator may have been called from a different region.
|
|
* Tagging regions for redrawing while drawing is rightfully prevented. However, this
|
|
* active button removing basically introduces handling logic to drawing code. So a
|
|
* notifier should be an acceptable workaround. */
|
|
WM_event_add_notifier_ex(wm, win, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
|
|
|
|
file_selflag = filelist_entry_select_get(sfile->files, file, CHECK_ALL);
|
|
}
|
|
}
|
|
|
|
/* file_selflag might have been modified by branch above. */
|
|
if ((file_selflag & FILE_SEL_EDITING) == 0) {
|
|
const int txpos = (params->display == FILE_IMGDISPLAY) ? tile_draw_rect.xmin :
|
|
tile_draw_rect.xmin + 1 + icon_ofs;
|
|
const int typos = (params->display == FILE_IMGDISPLAY) ?
|
|
tile_draw_rect.ymin + layout->tile_border_y + layout->textheight :
|
|
tile_draw_rect.ymax - layout->tile_border_y;
|
|
const int twidth = (params->display == FILE_IMGDISPLAY) ?
|
|
textwidth :
|
|
textwidth - 1 - icon_ofs - padx - layout->tile_border_x;
|
|
file_draw_string(txpos, typos, file->name, (float)twidth, textheight, align, text_col);
|
|
}
|
|
|
|
if (params->display != FILE_IMGDISPLAY) {
|
|
draw_details_columns(params, layout, file, &tile_draw_rect, text_col);
|
|
}
|
|
}
|
|
|
|
BLF_batch_draw_end();
|
|
|
|
UI_block_end(C, block);
|
|
UI_block_draw(C, block);
|
|
|
|
/* Draw last, on top of file list. */
|
|
if (draw_columnheader) {
|
|
draw_columnheader_background(layout, v2d);
|
|
draw_columnheader_columns(params, layout, v2d, text_col);
|
|
}
|
|
|
|
layout->curr_size = params->thumbnail_size;
|
|
}
|
|
|
|
static void file_draw_invalid_library_hint(const bContext *C,
|
|
const SpaceFile *sfile,
|
|
ARegion *region)
|
|
{
|
|
const FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile);
|
|
|
|
char library_ui_path[PATH_MAX];
|
|
file_path_to_ui_path(asset_params->base_params.dir, library_ui_path, sizeof(library_ui_path));
|
|
|
|
uchar text_col[4];
|
|
UI_GetThemeColor4ubv(TH_TEXT, text_col);
|
|
|
|
const View2D *v2d = ®ion->v2d;
|
|
const int pad = sfile->layout->tile_border_x;
|
|
const int width = BLI_rctf_size_x(&v2d->tot) - (2 * pad);
|
|
const int line_height = sfile->layout->textheight;
|
|
int sx = v2d->tot.xmin + pad;
|
|
/* For some reason no padding needed. */
|
|
int sy = v2d->tot.ymax;
|
|
|
|
{
|
|
const char *message = TIP_("Path to asset library does not exist:");
|
|
file_draw_string_multiline(sx, sy, message, width, line_height, text_col, NULL, &sy);
|
|
|
|
sy -= line_height;
|
|
file_draw_string(sx, sy, library_ui_path, width, line_height, UI_STYLE_TEXT_LEFT, text_col);
|
|
}
|
|
|
|
/* Separate a bit further. */
|
|
sy -= line_height * 2.2f;
|
|
|
|
{
|
|
UI_icon_draw(sx, sy - UI_UNIT_Y, ICON_INFO);
|
|
|
|
const char *suggestion = TIP_(
|
|
"Asset Libraries are local directories that can contain .blend files with assets inside.\n"
|
|
"Manage Asset Libraries from the File Paths section in Preferences");
|
|
file_draw_string_multiline(
|
|
sx + UI_UNIT_X, sy, suggestion, width - UI_UNIT_X, line_height, text_col, NULL, &sy);
|
|
|
|
uiBlock *block = UI_block_begin(C, region, __func__, UI_EMBOSS);
|
|
uiBut *but = uiDefIconTextButO(block,
|
|
UI_BTYPE_BUT,
|
|
"SCREEN_OT_userpref_show",
|
|
WM_OP_INVOKE_DEFAULT,
|
|
ICON_PREFERENCES,
|
|
NULL,
|
|
sx + UI_UNIT_X,
|
|
sy - line_height - UI_UNIT_Y * 1.2f,
|
|
UI_UNIT_X * 8,
|
|
UI_UNIT_Y,
|
|
NULL);
|
|
PointerRNA *but_opptr = UI_but_operator_ptr_get(but);
|
|
RNA_enum_set(but_opptr, "section", USER_SECTION_FILE_PATHS);
|
|
|
|
UI_block_end(C, block);
|
|
UI_block_draw(C, block);
|
|
}
|
|
}
|
|
|
|
bool file_draw_hint_if_invalid(const bContext *C, const SpaceFile *sfile, ARegion *region)
|
|
{
|
|
FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile);
|
|
/* Only for asset browser. */
|
|
if (!ED_fileselect_is_asset_browser(sfile)) {
|
|
return false;
|
|
}
|
|
/* Check if the library exists. */
|
|
if ((asset_params->asset_library_ref.type == ASSET_LIBRARY_LOCAL) ||
|
|
filelist_is_dir(sfile->files, asset_params->base_params.dir)) {
|
|
return false;
|
|
}
|
|
|
|
file_draw_invalid_library_hint(C, sfile, region);
|
|
|
|
return true;
|
|
}
|
|
|
|
void file_view_preview_grid_draw(const bContext *C, ARegion *region)
|
|
{
|
|
SpaceFile *sfile = CTX_wm_space_file(C);
|
|
// bScreen *screen = CTX_wm_screen(C);
|
|
View2D *v2d = ®ion->v2d;
|
|
|
|
const uiStyle *style = UI_style_get_dpi();
|
|
const float padding = style->panelouter;
|
|
uiBlock *block = UI_block_begin(C, region, __func__, UI_EMBOSS);
|
|
uiLayout *layout = UI_block_layout(
|
|
block,
|
|
UI_LAYOUT_VERTICAL,
|
|
UI_LAYOUT_PANEL,
|
|
padding,
|
|
-padding,
|
|
/* 3x (instead of 2x) padding to add extra space for the scrollbar on the right. */
|
|
region->winx - 3 * padding,
|
|
1,
|
|
0,
|
|
style);
|
|
|
|
// PointerRNA asset_space_ptr;
|
|
// RNA_pointer_create(&screen->id, &RNA_SpaceAssetBrowser, asset_space, &asset_space_ptr);
|
|
// PropertyRNA *active_asset_idx_prop = RNA_struct_find_property(&asset_space_ptr,
|
|
// "active_asset_idx");
|
|
|
|
file_grid_view_create_in_layout(sfile->files, v2d, layout);
|
|
|
|
/* Update main region View2d dimensions. */
|
|
int layout_width, layout_height;
|
|
UI_block_layout_resolve(block, &layout_width, &layout_height);
|
|
UI_view2d_totRect_set(v2d, layout_width, layout_height);
|
|
|
|
UI_block_end(C, block);
|
|
UI_block_draw(C, block);
|
|
}
|