diff --git a/source/blender/windowmanager/intern/wm_files.cc b/source/blender/windowmanager/intern/wm_files.cc index 0b96a199105..f51ca4725a6 100644 --- a/source/blender/windowmanager/intern/wm_files.cc +++ b/source/blender/windowmanager/intern/wm_files.cc @@ -90,6 +90,8 @@ #include "RNA_access.hh" #include "RNA_define.hh" +#include "GPU_immediate.h" + #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" #include "IMB_thumbs.h" @@ -830,6 +832,113 @@ static void wm_file_read_post(bContext *C, WM_cursor_wait(false); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name File Loading Dialog + * \{ */ + +/** + * Window callback to draw a darkened area behind the loading dialog. + */ +static void loading_dialog_background_cb(const wmWindow *win, void *) +{ + GPU_blend(GPU_BLEND_ALPHA); + GPUVertFormat *format = immVertexFormat(); + int pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + immUniformColor4f(0.0f, 0.0f, 0.0f, 0.5f); + immBegin(GPU_PRIM_TRI_STRIP, 4); + immVertex2f(pos, WM_window_pixels_x(win), 0.0f); + immVertex2f(pos, 0.0f, 0.0f); + immVertex2f(pos, WM_window_pixels_x(win), WM_window_pixels_y(win)); + immVertex2f(pos, 0.0f, WM_window_pixels_y(win)); + immEnd(); + immUnbindProgram(); +} + +/** + * Dialog displayed during blend file loading, showing thumbnail and file name. + */ +static uiBlock *wm_block_create_loading(bContext *C, ARegion *region, void *arg) +{ + const char *filepath = static_cast(arg); + short preview_width = int((114.0f * UI_SCALE_FAC)); + + /* First look for preview in the local thumbnail cache folder. */ + ImBuf *ibuf = IMB_thumb_read(filepath, THB_LARGE); + + if (!ibuf) { + /* Not in cache, so extract from the blend file itself. */ + BlendThumbnail *data = BLO_thumbnail_from_file(filepath); + ibuf = data ? BKE_main_thumbnail_to_imbuf(nullptr, data) : nullptr; + if (data) { + MEM_freeN(data); + } + } + + const uchar *color = nullptr; + + if (!ibuf) { + /* No thumbnails found, so just use Blender logo. */ + ibuf = UI_icon_alert_imbuf_get(ALERT_ICON_BLENDER); + color = UI_GetTheme()->tui.wcol_menu_back.text_sel; + IMB_premultiply_alpha(ibuf); + } + + /* Size to fit within preview_width x preview_width. */ + float scale = float(preview_width) / float(MAX2(ibuf->x, ibuf->y)); + IMB_scaleImBuf(ibuf, scale * ibuf->x, scale * ibuf->y); + preview_width = ibuf->x; + + const uiStyle *style = UI_style_get_dpi(); + const int text_points_max = MAX2(style->widget.points, style->widgetlabel.points); + const int dialog_width = preview_width + (text_points_max * 23 * UI_SCALE_FAC); + const float padding = 5.0f * UI_SCALE_FAC; + const float split_factor = (float(preview_width) + padding) / + float(dialog_width - style->columnspace); + + uiBlock *block = UI_block_begin(C, region, "file_open_loading_popup", UI_EMBOSS); + UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP); + + uiLayout *block_layout = UI_block_layout( + block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, dialog_width, 0, 0, style); + + /* Split layout to put preview on left side. */ + uiLayout *split_block = uiLayoutSplit(block_layout, split_factor, false); + + /* Preview on the left. */ + uiLayout *layout = uiLayoutRow(split_block, false); + /* Using 'align_left' with 'row' avoids stretching the icon along the width of column. */ + uiLayoutSetAlignment(layout, UI_LAYOUT_ALIGN_LEFT); + + uiDefButImage(block, ibuf, 0, U.widget_unit, ibuf->x, ibuf->y, color); + + /* The rest of the content on the right. */ + layout = uiLayoutColumn(split_block, false); + + uiItemS(layout); + + uiItemL_ex(layout, N_("Opening file..."), ICON_NONE, false, false); + + char filename[FILE_MAX]; + BLI_path_split_file_part(filepath, filename, sizeof(filename)); + uiItemL_ex(layout, filename, ICON_NONE, true, false); + + size_t filesize = BLI_file_size(filepath); + if (filesize > 0) { + char size_dec[BLI_STR_FORMAT_INT32_DECIMAL_UNIT_SIZE]; + BLI_str_format_decimal_unit(size_dec, filesize); + uiItemL_ex(layout, size_dec, ICON_NONE, false, false); + } + + uiItemS(layout); + + UI_block_bounds_set_centered(block, 5 * UI_SCALE_FAC); + + return block; +} + static void wm_read_callback_pre_wrapper(bContext *C, const char *filepath) { /* NOTE: either #BKE_CB_EVT_LOAD_POST or #BKE_CB_EVT_LOAD_POST_FAIL must run. @@ -1022,9 +1131,18 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports) CTX_wm_window_set(C, win); } if (win != nullptr) { - /* Redraw to remove any open menus. */ + /* Draw a darkened background across the window. */ + void *cb = WM_draw_cb_activate(win, loading_dialog_background_cb, nullptr); + + /* Set up the dialog. */ + UI_popup_block_invoke(C, wm_block_create_loading, (void *)filepath, nullptr); + + /* Redraw to remove any open menus and show loading block. */ WM_redraw_windows(C); + /* No more need for the window-darkening callback. */ + WM_draw_cb_exit(win, cb); + if (win_was_null) { CTX_wm_window_set(C, nullptr); }