UI: File/Asset Browser: Improve positioning of labels and icons

While working on #136907, I noticed issues with how we position and size
elements in the File/Asset Browser:
- A padding meant for outside the list "tiles" was applied inside, resulting
  in unnecessary/unintended extra space between previews and their label, and
  too small vertical paddings between tiles (see screenshots in PR).
- The height of text was measured based on the font size (not the actual line
  height) and scaled up by some arbitrary amount. Made it hard to draw
  multi-line text well, as done in #136907.
- Icons in list views were not centered vertically.
- The vertical list offset the entire list by the padding meant for tiles. Not
  quite sure if intentional, I think it looks better with it. But code was
  confusing since suddenly this extra tile padding was thrown in. Should be a
  separate padding.

All these changes are a bit intertwined, so submitting them together.

Basically this makes paddings be applied more consistently and fixes related
issues, so the code makes much more sense.

Pull Request: https://projects.blender.org/blender/blender/pulls/137675
This commit is contained in:
Julian Eisel
2025-04-18 11:34:01 +02:00
committed by Julian Eisel
parent 826abf2ddc
commit 529ab6555c
3 changed files with 90 additions and 78 deletions

View File

@@ -62,7 +62,13 @@ struct FileLayout {
int attribute_column_header_h;
int prv_w;
int prv_h;
/** Extra padding to add above any files. Used for horizontal and column list views. */
int list_padding_top;
/** Width to draw the file's "tile" (matches the highlight background) with. `tile_border_x` will
* be added before and after it as padding around the tile. */
int tile_w;
/** Height to draw the file's "tile" (matches the highlight background) with. `tile_border_y`
* will be added above and below it as padding around the tile. */
int tile_h;
int tile_border_x;
int tile_border_y;

View File

@@ -421,23 +421,13 @@ static uiBut *file_add_icon_but(const SpaceFile *sfile,
uiBut *but;
const int x = tile_draw_rect->xmin + padx;
const int y = tile_draw_rect->ymax - sfile->layout->tile_border_y -
round_fl_to_int((sfile->layout->tile_h + height) / 2.0f);
const int y = tile_draw_rect->ymin +
round_fl_to_int((BLI_rcti_size_y(tile_draw_rect) - height) / 2.0f);
if (icon < BIFICONID_LAST_STATIC) {
/* Small built-in icon. Draw centered in given width. */
but = uiDefIconBut(block,
UI_BTYPE_LABEL,
0,
icon,
x,
y + 1,
width,
height,
nullptr,
0.0f,
0.0f,
std::nullopt);
but = uiDefIconBut(
block, UI_BTYPE_LABEL, 0, icon, x, y, width, height, nullptr, 0.0f, 0.0f, std::nullopt);
/* Center the icon. */
UI_but_drawflag_disable(but, UI_BUT_ICON_LEFT);
}
@@ -963,7 +953,7 @@ static void draw_background(FileLayout *layout, View2D *v2d)
/* 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;
sy = int(v2d->cur.ymax) - layout->offset_top - i * item_height - layout->list_padding_top;
/* Offset pattern slightly to add scroll effect. */
sy += round_fl_to_int(item_height * (v2d->tot.ymax - v2d->cur.ymax) / item_height);
@@ -1189,7 +1179,7 @@ static void draw_details_columns(const FileSelectParams *params,
if (str) {
file_draw_string(sx + ATTRIBUTE_COLUMN_PADDING,
tile_draw_rect->ymax - layout->tile_border_y,
tile_draw_rect->ymax,
IFACE_(str),
column->width - 2 * ATTRIBUTE_COLUMN_PADDING,
layout->tile_h,
@@ -1212,7 +1202,32 @@ static rcti tile_draw_rect_get(const View2D *v2d, const FileLayout *layout, cons
rect.xmin = tile_pos_x;
rect.xmax = rect.xmin + layout->tile_w;
rect.ymax = tile_pos_y;
rect.ymin = rect.ymax - layout->tile_h - layout->tile_border_y;
rect.ymin = rect.ymax - layout->tile_h;
return rect;
}
/**
* Get the boundaries to display the name label in (this isn't the rectangle of the text itself).
*/
static rcti text_draw_rect_get(const View2D *v2d,
const eFileDisplayType display_type,
const FileLayout *layout,
const int file_idx,
const int icon_ofs_x)
{
rcti tile_rect = tile_draw_rect_get(v2d, layout, file_idx);
rcti rect = tile_rect;
if (display_type == FILE_IMGDISPLAY) {
rect.ymin += round_fl_to_int(layout->prv_border_y * 0.5f);
rect.ymax = rect.ymin + layout->textheight;
}
else {
rect.xmin += icon_ofs_x + 1;
rect.xmax = tile_rect.xmin + round_fl_to_int(layout->attribute_columns[COLUMN_NAME].width) -
layout->tile_border_x;
}
return rect;
}
@@ -1231,7 +1246,6 @@ void file_draw_list(const bContext *C, ARegion *region)
int numfiles;
int numfiles_layout;
int offset;
int column_width, textheight;
int i;
eFontStyle_Align align;
bool do_drag;
@@ -1262,11 +1276,6 @@ void file_draw_list(const bContext *C, ARegion *region)
filelist_file_cache_slidingwindow_set(files, numfiles_layout);
column_width = (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) {
@@ -1323,10 +1332,7 @@ void file_draw_list(const bContext *C, ARegion *region)
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, -U.pixelsize);
draw_tile_background(&tile_bg_rect, colorid, shade);
draw_tile_background(&tile_draw_rect, colorid, shade);
}
}
UI_draw_roundbox_corner_set(UI_CNR_NONE);
@@ -1383,15 +1389,18 @@ void file_draw_list(const bContext *C, ARegion *region)
if (do_drag) {
const uiStyle *style = UI_style_get();
const int str_width = UI_fontstyle_string_width(&style->widget, file->name);
const int drag_width = std::min(str_width + icon_ofs,
int(column_width - ATTRIBUTE_COLUMN_PADDING));
const int drag_width = std::min(
str_width + icon_ofs,
int(layout->attribute_columns[COLUMN_NAME].width - ATTRIBUTE_COLUMN_PADDING));
if (drag_width > 0) {
/* Uses full row height (tile height plus 2 * tile border padding) so there's no space
* between rows. */
uiBut *drag_but = uiDefBut(block,
UI_BTYPE_LABEL,
0,
"",
tile_draw_rect.xmin,
tile_draw_rect.ymin - 1,
tile_draw_rect.ymin - layout->tile_border_y,
drag_width,
layout->tile_h + layout->tile_border_y * 2,
nullptr,
@@ -1425,26 +1434,30 @@ void file_draw_list(const bContext *C, ARegion *region)
(filelist_loading || icon >= BIFICONID_LAST_STATIC))
{
const BIFIconID type_icon = filelist_geticon_file_type(files, i, true);
file_add_overlay_icon_but(
block, tile_draw_rect.xmin + padx - 2, tile_draw_rect.ymin - 1, type_icon);
file_add_overlay_icon_but(block,
tile_draw_rect.xmin + padx - 2,
tile_draw_rect.ymin - 2 * UI_SCALE_FAC,
type_icon);
}
}
if (file_selflag & FILE_SEL_EDITING) {
const short width = (params->display == FILE_IMGDISPLAY) ?
column_width :
layout->attribute_columns[COLUMN_NAME].width -
ATTRIBUTE_COLUMN_PADDING;
const rcti text_rect = text_draw_rect_get(
v2d, eFileDisplayType(params->display), layout, i, icon_ofs);
if (file_selflag & FILE_SEL_EDITING) {
const int but_height =
(params->display == FILE_IMGDISPLAY) ?
layout->textheight * 1.4f :
/* Just a little smaller than the tile height, clamped to #UI_UNIT_Y as maximum. */
std::min(short(BLI_rcti_size_y(&text_rect) - 1.0f * UI_SCALE_FAC), UI_UNIT_Y);
uiBut *but = uiDefBut(block,
UI_BTYPE_TEXT,
1,
"",
tile_draw_rect.xmin + icon_ofs,
tile_draw_rect.ymin + layout->tile_border_y +
((layout->tile_h - textheight) / 2.0f) - 0.15f * UI_UNIT_X,
width - icon_ofs,
textheight,
text_rect.xmin,
text_rect.ymin,
BLI_rcti_size_x(&text_rect),
but_height,
params->renamefile,
1.0f,
float(sizeof(params->renamefile)),
@@ -1471,17 +1484,13 @@ void file_draw_list(const bContext *C, ARegion *region)
/* 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) ?
column_width :
column_width - 1 - icon_ofs - padx - layout->tile_border_x;
const int theight = (params->display == FILE_IMGDISPLAY) ? textheight : layout->tile_h;
file_draw_string(txpos, typos, file->name, float(twidth), theight, align, text_col);
file_draw_string(text_rect.xmin,
text_rect.ymax,
file->name,
BLI_rcti_size_x(&text_rect),
BLI_rcti_size_y(&text_rect),
align,
text_col);
}
if (params->display != FILE_IMGDISPLAY) {

View File

@@ -762,7 +762,8 @@ int ED_fileselect_layout_numfiles(FileLayout *layout, ARegion *region)
}
const int y_item = layout->tile_h + (2 * layout->tile_border_y);
const int y_view = int(BLI_rctf_size_y(&region->v2d.cur)) - layout->offset_top;
const int y_view = int(BLI_rctf_size_y(&region->v2d.cur)) - layout->offset_top -
layout->list_padding_top;
const int y_over = y_item - (y_view % y_item);
numfiles = int(float(y_view + y_over) / float(y_item));
return numfiles * layout->flow_columns;
@@ -784,9 +785,11 @@ FileSelection ED_fileselect_layout_offset_rect(FileLayout *layout, const rcti *r
}
colmin = (rect->xmin) / (layout->tile_w + 2 * layout->tile_border_x);
rowmin = (rect->ymin - layout->offset_top) / (layout->tile_h + 2 * layout->tile_border_y);
rowmin = (rect->ymin - layout->offset_top - layout->list_padding_top) /
(layout->tile_h + 2 * layout->tile_border_y);
colmax = (rect->xmax) / (layout->tile_w + 2 * layout->tile_border_x);
rowmax = (rect->ymax - layout->offset_top) / (layout->tile_h + 2 * layout->tile_border_y);
rowmax = (rect->ymax - layout->offset_top - layout->list_padding_top) /
(layout->tile_h + 2 * layout->tile_border_y);
if (is_inside(colmin, rowmin, layout->flow_columns, layout->rows) ||
is_inside(colmax, rowmax, layout->flow_columns, layout->rows))
@@ -833,7 +836,8 @@ int ED_fileselect_layout_offset(FileLayout *layout, int x, int y)
}
offsetx = (x) / (layout->tile_w + 2 * layout->tile_border_x);
offsety = (y - layout->offset_top) / (layout->tile_h + 2 * layout->tile_border_y);
offsety = (y - layout->offset_top - layout->list_padding_top) /
(layout->tile_h + 2 * layout->tile_border_y);
if (offsetx > layout->flow_columns - 1) {
return -1;
@@ -879,13 +883,13 @@ void ED_fileselect_layout_tilepos(const FileLayout *layout, int tile, int *x, in
if (layout->flag == FILE_LAYOUT_HOR) {
*x = layout->tile_border_x +
(tile / layout->rows) * (layout->tile_w + 2 * layout->tile_border_x);
*y = layout->offset_top + layout->tile_border_y +
*y = layout->offset_top + layout->list_padding_top + layout->tile_border_y +
(tile % layout->rows) * (layout->tile_h + 2 * layout->tile_border_y);
}
else {
*x = layout->tile_border_x +
((tile) % layout->flow_columns) * (layout->tile_w + 2 * layout->tile_border_x);
*y = layout->offset_top + layout->tile_border_y +
*y = layout->offset_top + layout->list_padding_top + layout->tile_border_y +
((tile) / layout->flow_columns) * (layout->tile_h + 2 * layout->tile_border_y);
}
}
@@ -967,18 +971,8 @@ float file_string_width(const char *str)
float file_font_pointsize()
{
#if 0
float s;
char tmp[2] = "X";
const uiStyle *style = UI_style_get();
UI_fontstyle_set(&style->widget);
s = BLF_height(style->widget.uifont_id, tmp);
return style->widget.points;
#else
const uiStyle *style = UI_style_get();
UI_fontstyle_set(&style->widget);
return style->widget.points * UI_SCALE_FAC;
#endif
return UI_fontstyle_height_max(&style->widget);
}
static void file_attribute_columns_widths(const FileSelectParams *params, FileLayout *layout)
@@ -1047,7 +1041,6 @@ void ED_fileselect_init_layout(SpaceFile *sfile, ARegion *region)
FileLayout *layout = nullptr;
View2D *v2d = &region->v2d;
int numfiles;
int textheight;
if (sfile->layout == nullptr) {
sfile->layout = static_cast<FileLayout *>(MEM_callocN(sizeof(FileLayout), "file_layout"));
@@ -1058,7 +1051,7 @@ void ED_fileselect_init_layout(SpaceFile *sfile, ARegion *region)
}
numfiles = filelist_files_ensure(sfile->files);
textheight = int(file_font_pointsize());
const int textheight = int(file_font_pointsize());
layout = sfile->layout;
layout->textheight = textheight;
@@ -1070,6 +1063,7 @@ void ED_fileselect_init_layout(SpaceFile *sfile, ARegion *region)
layout->prv_h = (float(params->thumbnail_size) / 20.0f) * UI_UNIT_Y;
layout->tile_border_x = pad_fac * UI_UNIT_X;
layout->tile_border_y = pad_fac * UI_UNIT_X;
layout->list_padding_top = 0;
layout->prv_border_x = pad_fac * UI_UNIT_X;
layout->prv_border_y = pad_fac * UI_UNIT_Y;
layout->tile_w = layout->prv_w + 2 * layout->prv_border_x;
@@ -1095,14 +1089,16 @@ void ED_fileselect_init_layout(SpaceFile *sfile, ARegion *region)
layout->prv_w = ICON_DEFAULT_WIDTH_SCALE;
layout->prv_h = ICON_DEFAULT_HEIGHT_SCALE;
layout->tile_border_x = 0.4f * UI_UNIT_X;
layout->tile_border_y = 0.1f * UI_UNIT_Y;
layout->tile_h = textheight * 3 / 2;
layout->tile_border_y = 0.05f * UI_UNIT_Y;
layout->list_padding_top = 2 * layout->tile_border_y;
layout->tile_h = round_fl_to_int(textheight * 1.4f);
layout->width = int(BLI_rctf_size_x(&v2d->cur) - 2 * layout->tile_border_x);
layout->tile_w = layout->width;
layout->flow_columns = 1;
layout->attribute_column_header_h = layout->tile_h * 1.2f + 2 * layout->tile_border_y;
layout->offset_top = layout->attribute_column_header_h;
rowcount = int(BLI_rctf_size_y(&v2d->cur) - layout->offset_top - 2 * layout->tile_border_y) /
rowcount = int(BLI_rctf_size_y(&v2d->cur) - layout->offset_top -
2 * layout->list_padding_top) /
(layout->tile_h + 2 * layout->tile_border_y);
file_attribute_columns_init(params, layout);
@@ -1111,15 +1107,16 @@ void ED_fileselect_init_layout(SpaceFile *sfile, ARegion *region)
/* layout->rows can be zero if a very small area is changed to a File Browser. #124168. */
layout->height = sfile->layout->rows * (layout->tile_h + 2 * layout->tile_border_y) +
layout->tile_border_y * 2 + layout->offset_top;
layout->list_padding_top * 2 + layout->offset_top;
layout->flag = FILE_LAYOUT_VER;
}
else if (params->display == FILE_HORIZONTALDISPLAY) {
layout->prv_w = params->list_thumbnail_size * UI_SCALE_FAC;
layout->prv_h = params->list_thumbnail_size * UI_SCALE_FAC;
layout->tile_border_x = 0.4f * UI_UNIT_X;
layout->tile_border_y = 0.1f * UI_UNIT_Y;
layout->tile_h = std::max(textheight * 3 / 2, layout->prv_h);
layout->tile_border_y = 0.05f * UI_UNIT_Y;
layout->list_padding_top = 2 * layout->tile_border_y;
layout->tile_h = std::max(round_fl_to_int(textheight * 1.4f), layout->prv_h);
layout->attribute_column_header_h = 0;
layout->offset_top = layout->attribute_column_header_h;
layout->height = int(BLI_rctf_size_y(&v2d->cur) - 2 * layout->tile_border_y);