From 9dfef8c22987c12a6aad47a8913e60943d8578e7 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Fri, 14 May 2021 16:40:49 +0200 Subject: Move thumbnail loading/unloading to AsyncImageLoader --- include/AsyncImageLoader.hpp | 22 ++++++++--- include/Body.hpp | 3 -- src/AsyncImageLoader.cpp | 29 +++++++++++++- src/Body.cpp | 92 ++++++++++---------------------------------- src/QuickMedia.cpp | 55 +++++++++++++++++--------- 5 files changed, 101 insertions(+), 100 deletions(-) diff --git a/include/AsyncImageLoader.hpp b/include/AsyncImageLoader.hpp index 0384a71..7fe7c07 100644 --- a/include/AsyncImageLoader.hpp +++ b/include/AsyncImageLoader.hpp @@ -9,6 +9,7 @@ #include #include #include +#include namespace QuickMedia { enum class LoadingState { @@ -19,11 +20,10 @@ namespace QuickMedia { }; struct ThumbnailData { - bool referenced = false; LoadingState loading_state = LoadingState::NOT_LOADED; sf::Texture texture; std::unique_ptr image; // Set in another thread. This should be .reset after loading it into |texture|, to save memory - sf::Clock texture_applied_time; + size_t counter = 0; }; // This function is async @@ -34,10 +34,16 @@ namespace QuickMedia { class AsyncImageLoader { public: static AsyncImageLoader& get_instance(); - // Returns false if the image loader is already loading an image. In that case, this function should be called again later. + + // Never returns nullptr. Instead check the |loading_state| of the thumbnail data to see if it has finished loading. + // This function should be called every frame for the objects that need to display this thumbnail, otherwise it can be unloaded. // set |resize_target_size| to {0, 0} to disable resizing. // Note: this method is not thread-safe - void load_thumbnail(const std::string &url, bool local, sf::Vector2i resize_target_size, std::shared_ptr thumbnail_data); + std::shared_ptr get_thumbnail(const std::string &url, bool local, sf::Vector2i resize_target_size); + + // Note: this should only be called once every frame. + // Note: this method is not thread-safe + void update(); private: AsyncImageLoader(); ~AsyncImageLoader(); @@ -52,6 +58,10 @@ namespace QuickMedia { sf::Vector2i resize_target_size; }; + // set |resize_target_size| to {0, 0} to disable resizing. + // Note: this method is not thread-safe + void load_thumbnail(const std::string &url, bool local, sf::Vector2i resize_target_size, std::shared_ptr thumbnail_data); + // Returns -1 if all threads are busy int get_free_load_index() const; private: @@ -60,5 +70,7 @@ namespace QuickMedia { std::thread download_image_thread[NUM_IMAGE_LOAD_THREADS]; std::thread load_image_thread; MessageQueue image_load_queue; + std::unordered_map> thumbnails; + size_t counter = 0; }; -} \ No newline at end of file +} diff --git a/include/Body.hpp b/include/Body.hpp index 48eb45f..b5ca210 100644 --- a/include/Body.hpp +++ b/include/Body.hpp @@ -206,7 +206,6 @@ namespace QuickMedia { void insert_items_by_timestamps(BodyItems new_items); void clear_cache(); void clear_text_cache(); - void clear_thumbnails(); BodyItem* get_selected() const; std::shared_ptr get_selected_shared(); @@ -278,7 +277,6 @@ namespace QuickMedia { float get_offset_to_last_visible_item(sf::Vector2f body_size); private: Program *program; - std::unordered_map> item_thumbnail_textures; int selected_item; int prev_selected_item; double page_scroll; @@ -316,7 +314,6 @@ namespace QuickMedia { sf::Vector2f body_size; float selected_item_height = 0.0f; float selected_scrolled = 0.0f; - bool loaded_textures_changed = false; std::shared_ptr clicked_body_item = nullptr; RoundedRectangle item_background; RoundedRectangle reaction_background; diff --git a/src/AsyncImageLoader.cpp b/src/AsyncImageLoader.cpp index 947f637..1769846 100644 --- a/src/AsyncImageLoader.cpp +++ b/src/AsyncImageLoader.cpp @@ -7,6 +7,7 @@ #include "../external/hash-library/sha256.h" #include +#include #include namespace QuickMedia { @@ -212,6 +213,32 @@ namespace QuickMedia { }); } + std::shared_ptr AsyncImageLoader::get_thumbnail(const std::string &url, bool local, sf::Vector2i resize_target_size) { + // TODO: Instead of generating a new hash everytime to access thumbnail, cache the hash of the thumbnail url + auto &thumbnail_data = thumbnails[url]; + if(!thumbnail_data) + thumbnail_data = std::make_shared(); + thumbnail_data->counter = counter; + load_thumbnail(url, local, resize_target_size, thumbnail_data); + return thumbnail_data; + } + + void AsyncImageLoader::update() { + bool loaded_textures_changed = false; + for(auto it = thumbnails.begin(); it != thumbnails.end();) { + if(it->second->counter != counter) { + it = thumbnails.erase(it); + loaded_textures_changed = true; + } else { + ++it; + } + } + + ++counter; + if(loaded_textures_changed) + malloc_trim(0); + } + int AsyncImageLoader::get_free_load_index() const { for(int i = 0; i < NUM_IMAGE_LOAD_THREADS; ++i) { if(!loading_image[i]) @@ -219,4 +246,4 @@ namespace QuickMedia { } return -1; } -} \ No newline at end of file +} diff --git a/src/Body.cpp b/src/Body.cpp index 09d3051..803cece 100644 --- a/src/Body.cpp +++ b/src/Body.cpp @@ -144,7 +144,6 @@ namespace QuickMedia { } Body::~Body() { - item_thumbnail_textures.clear(); items.clear(); malloc_trim(0); } @@ -334,7 +333,7 @@ namespace QuickMedia { void Body::clear_cache() { clear_text_cache(); - clear_thumbnails(); + malloc_trim(0); } void Body::clear_text_cache() { @@ -345,11 +344,6 @@ namespace QuickMedia { } } - void Body::clear_thumbnails() { - item_thumbnail_textures.clear(); - malloc_trim(0); - } - BodyItem* Body::get_selected() const { if(selected_item < 0 || selected_item >= (int)items.size() || !items[selected_item]->visible) return nullptr; @@ -580,7 +574,6 @@ namespace QuickMedia { int num_items = items.size(); if(num_items == 0 || size.y <= 0.0f) { - item_thumbnail_textures.clear(); for(auto &body_item : items) { clear_body_item_cache(body_item.get()); if(body_item->embedded_item) @@ -596,10 +589,6 @@ namespace QuickMedia { return; } - for(auto &thumbnail_it : item_thumbnail_textures) { - thumbnail_it.second->referenced = false; - } - if(prev_selected_item < 0 || prev_selected_item >= (int)items.size()) { prev_selected_item = selected_item; } @@ -848,15 +837,6 @@ namespace QuickMedia { if(!items_cut_off_set) items_cut_off = false; - for(auto it = item_thumbnail_textures.begin(); it != item_thumbnail_textures.end();) { - if(!it->second->referenced) { - it = item_thumbnail_textures.erase(it); - loaded_textures_changed = true; - } else { - ++it; - } - } - // TODO: Only do this for items that are not visible, do not loop all items. // TODO: Improve performance! right now it can use up to 5-7% cpu with a lot of items! for(auto &body_item : items) { @@ -867,11 +847,6 @@ namespace QuickMedia { clear_body_item_cache(body_item->embedded_item.get()); } - if(loaded_textures_changed) { - loaded_textures_changed = false; - malloc_trim(0); - } - mouse_left_clicked = false; if(clicked_body_item) { auto clicked_body_item_tmp = clicked_body_item; // tmp because below call to body_item_select_callback may call this same draw function @@ -1116,18 +1091,10 @@ namespace QuickMedia { } void Body::draw_item(sf::RenderWindow &window, BodyItem *item, const sf::Vector2f &pos, const sf::Vector2f &size, const float item_height, const int item_index, const Json::Value &content_progress, bool include_embedded_item, bool merge_with_previous) { - // TODO: Instead of generating a new hash everytime to access textures, cache the hash of the thumbnail url + sf::Vector2i thumbnail_size = get_item_thumbnail_size(item); std::shared_ptr item_thumbnail; - if(draw_thumbnails && !item->thumbnail_url.empty()) { - auto item_thumbnail_it = item_thumbnail_textures.find(item->thumbnail_url); - if(item_thumbnail_it == item_thumbnail_textures.end()) { - item_thumbnail = std::make_shared(); - item_thumbnail_textures.insert(std::make_pair(item->thumbnail_url, item_thumbnail)); - } else { - item_thumbnail = item_thumbnail_it->second; - } - item_thumbnail->referenced = true; - } + if(draw_thumbnails && !item->thumbnail_url.empty()) + item_thumbnail = AsyncImageLoader::get_instance().get_thumbnail(item->thumbnail_url, item->thumbnail_is_local, thumbnail_size); if(body_item_render_callback && include_embedded_item) body_item_render_callback(item); @@ -1156,13 +1123,13 @@ namespace QuickMedia { } float text_offset_x = padding_x; - if(draw_thumbnails && item_thumbnail && !merge_with_previous) { + if(item_thumbnail && !merge_with_previous) { // TODO: Verify if this is safe. The thumbnail is being modified in another thread if(item_thumbnail->loading_state == LoadingState::APPLIED_TO_TEXTURE && item_thumbnail->texture.getNativeHandle() != 0) { image.setTexture(item_thumbnail->texture, true); auto image_size = image.getTexture()->getSize(); sf::Vector2f image_size_f(image_size.x, image_size.y); - sf::Vector2f content_size = to_vec2f(get_item_thumbnail_size(item)); + sf::Vector2f content_size = to_vec2f(thumbnail_size); auto new_image_size = clamp_to_size(image_size_f, content_size); auto image_scale = get_ratio(image_size_f, new_image_size); image.setScale(image_scale); @@ -1177,7 +1144,7 @@ namespace QuickMedia { // We want the next image fallback to have the same size as the successful image rendering, because its likely the image fallback will have the same size (for example thumbnails on youtube) //image_fallback.setSize(sf::Vector2f(width_ratio * image_size.x, height_ratio * image_size.y)); } else if(!item->thumbnail_url.empty()) { - sf::Vector2f content_size = to_vec2f(get_item_thumbnail_size(item)); + sf::Vector2f content_size = to_vec2f(thumbnail_size); sf::Color fallback_color(52, 58, 70); if(thumbnail_mask_shader && item->thumbnail_mask_type == ThumbnailMaskType::CIRCLE) { @@ -1204,7 +1171,7 @@ namespace QuickMedia { text_offset_x += image_padding_x + content_size.x; } } else if(item->thumbnail_size.x > 0) { - text_offset_x += image_padding_x + get_item_thumbnail_size(item).x; + text_offset_x += image_padding_x + thumbnail_size.x; } const float timestamp_text_y = std::floor(item_pos.y + padding_y - std::floor(6.0f * get_ui_scale())); @@ -1320,39 +1287,20 @@ namespace QuickMedia { if(load_texture) item->last_drawn_time = elapsed_time_sec; + sf::Vector2i content_size = get_item_thumbnail_size(item); float image_height = 0.0f; float text_offset_x = padding_x; - if(draw_thumbnails && !item->thumbnail_url.empty() && !merge_with_previous) { - sf::Vector2i content_size = get_item_thumbnail_size(item); + if(draw_thumbnails && load_texture && !item->thumbnail_url.empty() && !merge_with_previous) { image_height = content_size.y; - - std::shared_ptr item_thumbnail; - auto item_thumbnail_it = item_thumbnail_textures.find(item->thumbnail_url); - if(item_thumbnail_it == item_thumbnail_textures.end()) { - if(load_texture) { - item_thumbnail = std::make_shared(); - item_thumbnail_textures.insert(std::make_pair(item->thumbnail_url, item_thumbnail)); - } - } else { - item_thumbnail = item_thumbnail_it->second; - } - - if(load_texture && item_thumbnail) { - item_thumbnail->referenced = true; - - if(!item->thumbnail_url.empty() && item_thumbnail->loading_state == LoadingState::NOT_LOADED) - AsyncImageLoader::get_instance().load_thumbnail(item->thumbnail_url, item->thumbnail_is_local, content_size, item_thumbnail); - - if(item_thumbnail->loading_state == LoadingState::FINISHED_LOADING && item_thumbnail->image->getSize().x > 0 && item_thumbnail->image->getSize().y > 0) { - if(!item_thumbnail->texture.loadFromImage(*item_thumbnail->image)) - fprintf(stderr, "Warning: failed to load texture from image: %s\n", item->thumbnail_url.c_str()); - item_thumbnail->texture.setSmooth(true); - //item_thumbnail->texture.generateMipmap(); - item_thumbnail->image.reset(); - item_thumbnail->loading_state = LoadingState::APPLIED_TO_TEXTURE; - item_thumbnail->texture_applied_time.restart(); - loaded_textures_changed = true; - } + std::shared_ptr item_thumbnail = AsyncImageLoader::get_instance().get_thumbnail(item->thumbnail_url, item->thumbnail_is_local, content_size); + + if(item_thumbnail && item_thumbnail->loading_state == LoadingState::FINISHED_LOADING && item_thumbnail->image->getSize().x > 0 && item_thumbnail->image->getSize().y > 0) { + if(!item_thumbnail->texture.loadFromImage(*item_thumbnail->image)) + fprintf(stderr, "Warning: failed to load texture from image: %s\n", item->thumbnail_url.c_str()); + item_thumbnail->texture.setSmooth(true); + //item_thumbnail->texture.generateMipmap(); + item_thumbnail->image.reset(); + item_thumbnail->loading_state = LoadingState::APPLIED_TO_TEXTURE; } if(item_thumbnail && item_thumbnail->loading_state == LoadingState::APPLIED_TO_TEXTURE && item_thumbnail->texture.getNativeHandle() != 0) { @@ -1365,7 +1313,7 @@ namespace QuickMedia { text_offset_x += image_padding_x + content_size.x; } } else if(item->thumbnail_size.x > 0) { - text_offset_x += image_padding_x + get_item_thumbnail_size(item).x; + text_offset_x += image_padding_x + content_size.x; } if(load_texture) diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index a58b3d8..903c966 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -1547,7 +1547,6 @@ namespace QuickMedia { if(tabs[selected_tab].page->is_single_page()) { if(tabs[selected_tab].search_bar) tabs[selected_tab].search_bar->clear(); if(new_tabs.size() == 1 && !new_tabs[0].page) { - tabs[selected_tab].body->clear_thumbnails(); tabs[selected_tab].body = std::move(new_tabs[0].body); tabs[selected_tab].page->submit_body_item = prev_selected_item; return; @@ -1871,7 +1870,6 @@ namespace QuickMedia { if(associated_data.fetch_status == FetchStatus::LOADING && associated_data.fetch_type == FetchType::SEARCH && associated_data.fetch_future.ready()) { if(!associated_data.search_text_updated) { FetchResult fetch_result = associated_data.fetch_future.get(); - tabs[i].body->clear_thumbnails(); tabs[i].body->items = std::move(fetch_result.body_items); tabs[i].body->select_first_item(); associated_data.fetched_page = 0; @@ -1924,6 +1922,7 @@ namespace QuickMedia { window.clear(back_color); page_loop_render(window, tabs, selected_tab, tab_associated_data[selected_tab], json_chapters, ui_tabs); + AsyncImageLoader::get_instance().update(); window.display(); if(!tabs[selected_tab].body->items.empty() && tabs[selected_tab].body->is_last_item_fully_visible()) @@ -2442,6 +2441,8 @@ namespace QuickMedia { break; } + AsyncImageLoader::get_instance().update(); + if(!video_loaded) { window.clear(back_color); load_sprite.setPosition(window_size.x * 0.5f, window_size.y * 0.5f); @@ -2697,6 +2698,7 @@ namespace QuickMedia { load_sprite.setPosition(window_size.x * 0.5f, window_size.y * 0.5f); load_sprite.setRotation(load_sprite_timer.getElapsedTime().asSeconds() * 400.0); window.draw(load_sprite); + AsyncImageLoader::get_instance().update(); window.display(); } } @@ -2952,6 +2954,7 @@ namespace QuickMedia { current_page = PageType::IMAGES; break; } + AsyncImageLoader::get_instance().update(); window.display(); int focused_page = image_viewer.get_focused_page(); @@ -3071,6 +3074,8 @@ namespace QuickMedia { }); }; + bool redraw = true; + Entry comment_input("Press i to begin writing a comment...", &rounded_rectangle_shader); comment_input.draw_background = false; comment_input.set_editable(false); @@ -3151,7 +3156,8 @@ namespace QuickMedia { sf::Vector2f logo_size(std::floor(plugin_logo.getSize().x * logo_sprite.getScale().x), std::floor(plugin_logo.getSize().y * logo_sprite.getScale().y)); sf::Sprite file_to_upload_sprite; - auto file_to_upload_thumbnail_data = std::make_shared(); + bool sprite_applied_texture = false; + std::shared_ptr file_to_upload_thumbnail_data; const float logo_file_to_upload_spacing = std::floor(10.0f * get_ui_scale()); @@ -3163,7 +3169,6 @@ namespace QuickMedia { sf::Vector2f body_pos; sf::Vector2f body_size; - bool redraw = true; sf::Event event; std::stack comment_navigation_stack; @@ -3408,22 +3413,28 @@ namespace QuickMedia { update_idle_state(); handle_window_close(); - if(!selected_file_for_upload.empty() && file_to_upload_thumbnail_data->loading_state == LoadingState::NOT_LOADED) - AsyncImageLoader::get_instance().load_thumbnail(selected_file_for_upload, true, sf::Vector2i(logo_size.x, logo_size.y * 4), file_to_upload_thumbnail_data); - - if(selected_file_for_upload.empty() && file_to_upload_thumbnail_data->loading_state == LoadingState::APPLIED_TO_TEXTURE) { - file_to_upload_thumbnail_data = std::make_unique(); - redraw = true; + if(selected_file_for_upload.empty()) { + if(file_to_upload_thumbnail_data) { + file_to_upload_thumbnail_data.reset(); + redraw = true; + } + } else { + file_to_upload_thumbnail_data = AsyncImageLoader::get_instance().get_thumbnail(selected_file_for_upload, true, sf::Vector2i(logo_size.x, logo_size.y * 4)); } - if(file_to_upload_thumbnail_data->loading_state == LoadingState::FINISHED_LOADING && file_to_upload_thumbnail_data->image->getSize().x > 0 && file_to_upload_thumbnail_data->image->getSize().y > 0) { + if(file_to_upload_thumbnail_data && file_to_upload_thumbnail_data->loading_state == LoadingState::FINISHED_LOADING && file_to_upload_thumbnail_data->image->getSize().x > 0 && file_to_upload_thumbnail_data->image->getSize().y > 0) { if(!file_to_upload_thumbnail_data->texture.loadFromImage(*file_to_upload_thumbnail_data->image)) fprintf(stderr, "Warning: failed to load texture for attached file\n"); file_to_upload_thumbnail_data->texture.setSmooth(true); //room_avatar_thumbnail_data->texture.generateMipmap(); file_to_upload_thumbnail_data->image.reset(); file_to_upload_thumbnail_data->loading_state = LoadingState::APPLIED_TO_TEXTURE; + sprite_applied_texture = false; + } + + if(file_to_upload_thumbnail_data && file_to_upload_thumbnail_data->loading_state == LoadingState::APPLIED_TO_TEXTURE && !sprite_applied_texture) { file_to_upload_sprite.setTexture(file_to_upload_thumbnail_data->texture, true); + sprite_applied_texture = true; sf::Vector2f texture_size_f(file_to_upload_thumbnail_data->texture.getSize().x, file_to_upload_thumbnail_data->texture.getSize().y); sf::Vector2f image_scale = get_ratio(texture_size_f, clamp_to_size_x(texture_size_f, logo_size)); @@ -3432,7 +3443,7 @@ namespace QuickMedia { } float chat_input_height_full_images = logo_size.y; - if(file_to_upload_thumbnail_data->loading_state == LoadingState::APPLIED_TO_TEXTURE) { + if(file_to_upload_thumbnail_data && file_to_upload_thumbnail_data->loading_state == LoadingState::APPLIED_TO_TEXTURE) { const float file_to_upload_height = std::floor(logo_file_to_upload_spacing + file_to_upload_sprite.getTexture()->getSize().y * file_to_upload_sprite.getScale().y); chat_input_height_full_images += file_to_upload_height; } @@ -3554,18 +3565,19 @@ namespace QuickMedia { } else if(navigation_stage == NavigationStage::REPLYING) { window.draw(comment_input_shade); window.draw(logo_sprite); - if(file_to_upload_thumbnail_data->loading_state == LoadingState::APPLIED_TO_TEXTURE) + if(file_to_upload_thumbnail_data && file_to_upload_thumbnail_data->loading_state == LoadingState::APPLIED_TO_TEXTURE) window.draw(file_to_upload_sprite); comment_input.draw(window); thread_body->draw(window, body_pos, body_size); } else if(navigation_stage == NavigationStage::VIEWING_COMMENTS) { window.draw(comment_input_shade); window.draw(logo_sprite); - if(file_to_upload_thumbnail_data->loading_state == LoadingState::APPLIED_TO_TEXTURE) + if(file_to_upload_thumbnail_data && file_to_upload_thumbnail_data->loading_state == LoadingState::APPLIED_TO_TEXTURE) window.draw(file_to_upload_sprite); comment_input.draw(window); thread_body->draw(window, body_pos, body_size); } + AsyncImageLoader::get_instance().update(); window.display(); } } @@ -3672,6 +3684,7 @@ namespace QuickMedia { inputs[i]->update(); inputs[i]->draw(window, background.get_size() - sf::Vector2f(padding_x * 2.0f, 0.0f), false); } + AsyncImageLoader::get_instance().update(); window.display(); } } @@ -4170,7 +4183,6 @@ namespace QuickMedia { std::string room_topic = current_room->get_topic(); room_name_text.setString(sf::String::fromUtf8(room_name.begin(), room_name.end())); room_topic_text.setString(sf::String::fromUtf8(room_topic.begin(), room_topic.end())); - room_avatar_thumbnail_data = std::make_shared(); read_marker_timeout_ms = 0; redraw = true; @@ -5215,8 +5227,8 @@ namespace QuickMedia { typing_state_queue.push(false); } - if(current_room && current_room->body_item && room_avatar_thumbnail_data->loading_state == LoadingState::NOT_LOADED) - AsyncImageLoader::get_instance().load_thumbnail(current_room->body_item->thumbnail_url, false, AVATAR_THUMBNAIL_SIZE, room_avatar_thumbnail_data); + if(!current_room->body_item->thumbnail_url.empty()) + room_avatar_thumbnail_data = AsyncImageLoader::get_instance().get_thumbnail(current_room->body_item->thumbnail_url, false, AVATAR_THUMBNAIL_SIZE); if(room_avatar_thumbnail_data->loading_state == LoadingState::FINISHED_LOADING && room_avatar_thumbnail_data->image->getSize().x > 0 && room_avatar_thumbnail_data->image->getSize().y > 0) { if(!room_avatar_thumbnail_data->texture.loadFromImage(*room_avatar_thumbnail_data->image)) @@ -5225,8 +5237,10 @@ namespace QuickMedia { //room_avatar_thumbnail_data->texture.generateMipmap(); room_avatar_thumbnail_data->image.reset(); room_avatar_thumbnail_data->loading_state = LoadingState::APPLIED_TO_TEXTURE; - room_avatar_sprite.setTexture(room_avatar_thumbnail_data->texture, true); + } + if(room_avatar_thumbnail_data->loading_state == LoadingState::APPLIED_TO_TEXTURE && !room_avatar_sprite.getTexture()) { + room_avatar_sprite.setTexture(room_avatar_thumbnail_data->texture, true); auto texture_size = room_avatar_sprite.getTexture()->getSize(); if(texture_size.x > 0 && texture_size.y > 0) { float width_ratio = (float)texture_size.x / (float)texture_size.y; @@ -5234,6 +5248,7 @@ namespace QuickMedia { float width_scale = height_scale * width_ratio; room_avatar_sprite.setScale(width_scale * get_ui_scale(), height_scale * get_ui_scale()); } + redraw = true; } const int selected_tab = ui_tabs.get_selected(); @@ -5549,6 +5564,7 @@ namespace QuickMedia { } } + AsyncImageLoader::get_instance().update(); window.display(); if(selected_tab == MESSAGES_TAB_INDEX) @@ -6158,6 +6174,7 @@ namespace QuickMedia { window.draw(status_text); window.draw(filename_text); window.draw(download_speed_text); + AsyncImageLoader::get_instance().update(); window.display(); frame_timer.restart(); } @@ -6233,7 +6250,6 @@ namespace QuickMedia { if(task_result == TaskResult::TRUE) { if(!new_tabs.empty()) { - file_manager_body->clear_thumbnails(); file_manager_body->items = std::move(new_tabs[0].body->items); file_manager_body->select_first_item(); search_bar->clear(); @@ -6371,6 +6387,7 @@ namespace QuickMedia { save_button.draw(window); file_name_entry.draw(window); + AsyncImageLoader::get_instance().update(); window.display(); } -- cgit v1.2.3