From bb049a4d89535202b4d525d1db27421d31c0af22 Mon Sep 17 00:00:00 2001 From: Michael Webster Date: Tue, 19 May 2020 22:38:46 -0400 Subject: [PATCH] thumbnails: generate/load thumbnails on visible items only, and drop background loading. Views load much more quickly when delaying any thumbnail processing until all items are fully loaded. An attempt is made to load 'ahead' and 'behind' the visible range, but in the list view, the current api does not allow scanning for rows 'above' the current view position, only below. As a result, scrolling a view down in the list view will reveal icons that have already been queued to load their thumbnail, but scrolling up will not. This behavior works properly in the icon views in both directions. --- libnemo-private/nemo-directory-async.c | 5 +- libnemo-private/nemo-file-private.h | 5 +- libnemo-private/nemo-file.c | 42 +++++- libnemo-private/nemo-file.h | 4 +- libnemo-private/nemo-icon-container.c | 171 +++++++++++++++++-------- libnemo-private/nemo-icon-container.h | 8 +- libnemo-private/nemo-icon-private.h | 9 +- libnemo-private/nemo-icon.h | 2 + libnemo-private/nemo-thumbnails.c | 24 +++- libnemo-private/nemo-thumbnails.h | 2 +- src/nemo-icon-view-container.c | 25 ++-- src/nemo-icon-view-grid-container.c | 8 +- src/nemo-icon-view.c | 4 +- src/nemo-list-model.c | 19 ++- src/nemo-list-model.h | 1 + src/nemo-list-view.c | 164 +++++++++++++++++++++--- src/nemo-view.c | 8 +- 17 files changed, 392 insertions(+), 109 deletions(-) diff --git a/libnemo-private/nemo-directory-async.c b/libnemo-private/nemo-directory-async.c index b40a51d5b..28cfc318d 100644 --- a/libnemo-private/nemo-directory-async.c +++ b/libnemo-private/nemo-directory-async.c @@ -1645,7 +1645,7 @@ lacks_btime (NemoFile *file) static gboolean lacks_filesystem_info (NemoFile *file) { - return !file->details->filesystem_info_is_up_to_date; + return nemo_file_is_directory (file) && !file->details->filesystem_info_is_up_to_date; } static gboolean @@ -1692,7 +1692,8 @@ lacks_extension_info (NemoFile *file) static gboolean lacks_thumbnail (NemoFile *file) { - return nemo_file_should_show_thumbnail (file) && + return file->details->load_thumb && + nemo_file_should_show_thumbnail (file) && file->details->thumbnail_path != NULL && !file->details->thumbnail_is_up_to_date; } diff --git a/libnemo-private/nemo-file-private.h b/libnemo-private/nemo-file-private.h index 179d9911f..981a7087e 100644 --- a/libnemo-private/nemo-file-private.h +++ b/libnemo-private/nemo-file-private.h @@ -56,6 +56,8 @@ struct NemoFileDetails eel_ref_str name; + gchar *cached_uri; + /* File info: */ GFileType type; @@ -221,7 +223,8 @@ struct NemoFileDetails eel_boolean_bit filesystem_readonly : 1; eel_boolean_bit filesystem_use_preview : 2; /* GFilesystemPreviewType */ - eel_boolean_bit filesystem_info_is_up_to_date : 1; + eel_boolean_bit filesystem_info_is_up_to_date : 1; + eel_boolean_bit load_thumb : 1; NemoFilePinning pinning; diff --git a/libnemo-private/nemo-file.c b/libnemo-private/nemo-file.c index d13bac294..ed34fea91 100644 --- a/libnemo-private/nemo-file.c +++ b/libnemo-private/nemo-file.c @@ -501,6 +501,7 @@ nemo_file_clear_info (NemoFile *file) file->details->ctime = 0; file->details->btime = 0; file->details->trash_time = 0; + file->details->load_thumb = FALSE; g_free (file->details->symlink_name); file->details->symlink_name = NULL; eel_ref_str_unref (file->details->mime_type); @@ -564,7 +565,7 @@ nemo_file_new_from_filename (NemoDirectory *directory, file->details->name = eel_ref_str_new (filename); #ifdef NEMO_FILE_DEBUG_REF - DEBUG_REF_PRINTF("%10p ref'd", file); + DEBUG_REF_PRINTF("%10p ref'd\n", file); #endif return file; @@ -676,7 +677,7 @@ nemo_file_new_from_info (NemoDirectory *directory, update_info_and_name (file, info); #ifdef NEMO_FILE_DEBUG_REF - DEBUG_REF_PRINTF("%10p ref'd", file); + DEBUG_REF_PRINTF("%10p ref'd\n", file); #endif return file; @@ -865,6 +866,8 @@ finalize (GObject *object) metadata_hash_free (file->details->metadata); } + g_free (file->details->cached_uri); + G_OBJECT_CLASS (nemo_file_parent_class)->finalize (object); } @@ -877,7 +880,7 @@ nemo_file_ref (NemoFile *file) g_return_val_if_fail (NEMO_IS_FILE (file), NULL); #ifdef NEMO_FILE_DEBUG_REF - DEBUG_REF_PRINTF("%10p ref'd", file); + DEBUG_REF_PRINTF("%10p ref'd\n", file); #endif return g_object_ref (file); @@ -893,7 +896,7 @@ nemo_file_unref (NemoFile *file) g_return_if_fail (NEMO_IS_FILE (file)); #ifdef NEMO_FILE_DEBUG_REF - DEBUG_REF_PRINTF("%10p unref'd", file); + DEBUG_REF_PRINTF("%10p unref'd\n", file); #endif g_object_unref (file); @@ -1639,6 +1642,18 @@ nemo_file_get_uri (NemoFile *file) return uri; } +const char * +nemo_file_peek_uri (NemoFile *file) +{ + g_return_val_if_fail (NEMO_IS_FILE (file), NULL); + + if (file->details->cached_uri == NULL) { + file->details->cached_uri = nemo_file_get_uri (file); + } + + return file->details->cached_uri; +} + /* Return the actual path associated with the passed-in file. */ char * nemo_file_get_path (NemoFile *file) @@ -4342,6 +4357,12 @@ nemo_file_delete_thumbnail (NemoFile *file) } } +gboolean +nemo_file_has_loaded_thumbnail (NemoFile *file) +{ + return file->details->thumbnail_is_up_to_date; +} + static void prepend_icon_name (const char *name, GThemedIcon *icon) @@ -4636,7 +4657,7 @@ nemo_file_get_icon (NemoFile *file, DEBUG ("Called file_get_icon(), at size %d, force thumbnail %d", size, flags & NEMO_FILE_ICON_FLAGS_FORCE_THUMBNAIL_SIZE); - if (flags & NEMO_FILE_ICON_FLAGS_USE_THUMBNAILS && + if ((flags & NEMO_FILE_ICON_FLAGS_USE_THUMBNAILS) && nemo_file_should_show_thumbnail (file)) { if (flags & NEMO_FILE_ICON_FLAGS_FORCE_THUMBNAIL_SIZE) { @@ -4727,12 +4748,12 @@ nemo_file_get_icon (NemoFile *file, !file->details->is_thumbnailing && !file->details->thumbnailing_failed) { if (nemo_can_thumbnail (file)) { - nemo_create_thumbnail (file, get_throttle_count (file)); + nemo_create_thumbnail (file, get_throttle_count (file), TRUE); } } } - if (file->details->is_thumbnailing && + if (file->details->thumbnail_path == NULL && nemo_can_thumbnail (file) && flags & NEMO_FILE_ICON_FLAGS_USE_THUMBNAILS) gicon = g_themed_icon_new (ICON_NAME_THUMBNAIL_LOADING); else @@ -8019,6 +8040,13 @@ nemo_file_invalidate_extension_info_internal (NemoFile *file) nemo_module_get_extensions_for_type (NEMO_TYPE_INFO_PROVIDER); } +void +nemo_file_set_load_thumb (NemoFile *file, + gboolean load_thumb) +{ + file->details->load_thumb = load_thumb; +} + void nemo_file_invalidate_attributes_internal (NemoFile *file, NemoFileAttributes file_attributes) diff --git a/libnemo-private/nemo-file.h b/libnemo-private/nemo-file.h index 8d1ab437c..ac39c6a4c 100644 --- a/libnemo-private/nemo-file.h +++ b/libnemo-private/nemo-file.h @@ -189,6 +189,7 @@ const char * nemo_file_peek_name (NemoFile GFile * nemo_file_get_location (NemoFile *file); char * nemo_file_get_description (NemoFile *file); char * nemo_file_get_uri (NemoFile *file); +const char * nemo_file_peek_uri (NemoFile *file); char * nemo_file_get_path (NemoFile *file); char * nemo_file_get_uri_scheme (NemoFile *file); NemoFile * nemo_file_get_parent (NemoFile *file); @@ -240,6 +241,7 @@ NemoRequestStatus nemo_file_get_deep_counts (NemoFile gboolean force); gboolean nemo_file_should_show_thumbnail (NemoFile *file); void nemo_file_delete_thumbnail (NemoFile *file); +gboolean nemo_file_has_loaded_thumbnail (NemoFile *file); gboolean nemo_file_should_show_directory_item_count (NemoFile *file); gboolean nemo_file_should_show_type (NemoFile *file); GList * nemo_file_get_keywords (NemoFile *file); @@ -521,7 +523,7 @@ void nemo_file_set_is_desktop_orphan (NemoFile *file, gboolean is_d gboolean nemo_file_get_pinning (NemoFile *file); void nemo_file_set_pinning (NemoFile *file, gboolean pin); - +void nemo_file_set_load_thumb (NemoFile *file, gboolean load_thumb); /* Debugging */ void nemo_file_dump (NemoFile *file); diff --git a/libnemo-private/nemo-icon-container.c b/libnemo-private/nemo-icon-container.c index 71890d6ee..323aa00d8 100644 --- a/libnemo-private/nemo-icon-container.c +++ b/libnemo-private/nemo-icon-container.c @@ -30,12 +30,14 @@ #include "nemo-icon-container.h" #include "nemo-file.h" +#include "nemo-directory.h" #include "nemo-desktop-icon-file.h" #include "nemo-global-preferences.h" #include "nemo-icon-private.h" #include "nemo-lib-self-check-functions.h" #include "nemo-selection-canvas-item.h" #include "nemo-desktop-utils.h" +#include "nemo-thumbnails.h" #include #include #include @@ -70,6 +72,9 @@ */ #define MAX_CLICK_TIME 1500 +#define INITIAL_UPDATE_VISIBLE_DELAY 300 +#define NORMAL_UPDATE_VISIBLE_DELAY 50 + /* Button assignments. */ #define DRAG_BUTTON 1 #define RUBBERBAND_BUTTON 1 @@ -120,7 +125,7 @@ static void handle_hadjustment_changed (GtkAdjustme static void handle_vadjustment_changed (GtkAdjustment *adjustment, NemoIconContainer *container); static GList * nemo_icon_container_get_selected_icons (NemoIconContainer *container); -static void nemo_icon_container_update_visible_icons (NemoIconContainer *container); +static void queue_update_visible_icons (NemoIconContainer *container, gint delay); static void reveal_icon (NemoIconContainer *container, NemoIcon *icon); @@ -1019,10 +1024,10 @@ redo_layout_internal (NemoIconContainer *container) } nemo_icon_container_update_scroll_region (container); + queue_update_visible_icons (container, INITIAL_UPDATE_VISIBLE_DELAY); process_pending_icon_to_reveal (container); process_pending_icon_to_rename (container); - nemo_icon_container_update_visible_icons (container); } static gboolean @@ -2729,6 +2734,11 @@ destroy (GtkWidget *object) container->details->size_allocation_count_id = 0; } + if (container->details->update_visible_icons_id > 0) { + g_source_remove (container->details->update_visible_icons_id); + container->details->update_visible_icons_id = 0; + } + /* destroy interactive search dialog */ if (container->details->search_window) { gtk_widget_destroy (container->details->search_window); @@ -4441,7 +4451,8 @@ real_move_icon (NemoIconContainer *container, static void real_update_icon (NemoIconContainer *container, - NemoIcon *icon) + NemoIcon *icon, + gboolean visible) { g_assert_not_reached (); } @@ -4955,6 +4966,9 @@ nemo_icon_container_init (NemoIconContainer *container) details->current_selection_count = -1; details->renaming_allocation_count = 0; + details->update_visible_icons_id = 0; + details->ok_to_load_thumbs = FALSE; + details->h_adjust = 100; details->v_adjust = 100; } @@ -5206,6 +5220,8 @@ nemo_icon_container_clear (NemoIconContainer *container) details->stretch_icon = NULL; details->drop_target = NULL; + details->ok_to_load_thumbs = FALSE; + for (p = details->icons; p != NULL; p = p->next) { icon_free (p->data); } @@ -5527,14 +5543,15 @@ nemo_icon_container_get_icon_images (NemoIconContainer *container, NemoIconData *data, int size, gboolean for_drag_accept, - gboolean *has_open_window) + gboolean *has_open_window, + gboolean visible) { NemoIconContainerClass *klass; klass = NEMO_ICON_CONTAINER_GET_CLASS (container); g_assert (klass->get_icon_images != NULL); - return klass->get_icon_images (container, data, size, for_drag_accept, has_open_window); + return klass->get_icon_images (container, data, size, for_drag_accept, has_open_window, visible); } static void @@ -5571,8 +5588,8 @@ nemo_icon_container_prioritize_thumbnailing (NemoIconContainer *container, klass->prioritize_thumbnailing (container, icon->data); } -static void -nemo_icon_container_update_visible_icons (NemoIconContainer *container) +static gboolean +update_visible_icons_cb (NemoIconContainer *container) { GtkAdjustment *vadj, *hadj; double min_y, max_y; @@ -5583,6 +5600,8 @@ nemo_icon_container_update_visible_icons (NemoIconContainer *container) gboolean visible; GtkAllocation allocation; + container->details->update_visible_icons_id = 0; + hadj = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (container)); vadj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (container)); gtk_widget_get_allocation (GTK_WIDGET (container), &allocation); @@ -5598,9 +5617,6 @@ nemo_icon_container_update_visible_icons (NemoIconContainer *container) eel_canvas_c2w (EEL_CANVAS (container), max_x, max_y, &max_x, &max_y); - /* Do the iteration in reverse to get the render-order from top to - * bottom for the prioritized thumbnails. - */ for (node = g_list_last (container->details->icons); node != NULL; node = node->prev) { icon = node->data; @@ -5617,21 +5633,55 @@ nemo_icon_container_update_visible_icons (NemoIconContainer *container) &x1, &y1); + gint overshoot; + if (nemo_icon_container_is_layout_vertical (container)) { - visible = x1 >= min_x && x0 <= max_x; + overshoot = (max_x - min_x) / 2; + + visible = x1 >= min_x - overshoot && x0 <= max_x + overshoot; } else { - visible = y1 >= min_y && y0 <= max_y; + overshoot = (max_y - min_y) / 2; + + visible = y1 >= min_y - overshoot && y0 <= max_y + overshoot; } if (visible) { nemo_icon_canvas_item_set_is_visible (icon->item, TRUE); - nemo_icon_container_prioritize_thumbnailing (container, - icon); + + if (!icon->ok_to_show_thumb) { + NemoFile *file = NEMO_FILE (icon->data); + + icon->ok_to_show_thumb = TRUE; + nemo_file_set_load_thumb (file, TRUE); + + if (nemo_file_is_thumbnailing (file)) { + nemo_icon_container_prioritize_thumbnailing (container, icon); + } else { + nemo_file_invalidate_attributes (file, NEMO_FILE_ATTRIBUTES_FOR_ICON); + } + } + + nemo_icon_container_update_icon (container, icon); } else { nemo_icon_canvas_item_set_is_visible (icon->item, FALSE); } } } + + return G_SOURCE_REMOVE; +} + +static void +queue_update_visible_icons(NemoIconContainer *container, + gint delay) +{ + NemoIconContainerDetails *details = container->details; + + if (details->update_visible_icons_id > 0) { + g_source_remove (details->update_visible_icons_id); + } + + details->update_visible_icons_id = g_timeout_add (delay, (GSourceFunc) update_visible_icons_cb, container); } static void @@ -5639,7 +5689,7 @@ handle_vadjustment_changed (GtkAdjustment *adjustment, NemoIconContainer *container) { if (!nemo_icon_container_is_layout_vertical (container)) { - nemo_icon_container_update_visible_icons (container); + queue_update_visible_icons (container, NORMAL_UPDATE_VISIBLE_DELAY); } } @@ -5648,7 +5698,7 @@ handle_hadjustment_changed (GtkAdjustment *adjustment, NemoIconContainer *container) { if (nemo_icon_container_is_layout_vertical (container)) { - nemo_icon_container_update_visible_icons (container); + queue_update_visible_icons (container, NORMAL_UPDATE_VISIBLE_DELAY); } } @@ -7825,40 +7875,6 @@ nemo_icon_container_accessible_get_type (void) return type; } -#if ! defined (NEMO_OMIT_SELF_CHECK) - -static char * -check_compute_stretch (int icon_x, int icon_y, int icon_size, - int start_pointer_x, int start_pointer_y, - int end_pointer_x, int end_pointer_y) -{ - StretchState start, current; - - start.icon_x = icon_x; - start.icon_y = icon_y; - start.icon_size = icon_size; - start.pointer_x = start_pointer_x; - start.pointer_y = start_pointer_y; - current.pointer_x = end_pointer_x; - current.pointer_y = end_pointer_y; - - compute_stretch (&start, ¤t); - - return g_strdup_printf ("%d,%d:%d", - current.icon_x, - current.icon_y, - current.icon_size); -} - -void -nemo_self_check_icon_container (void) -{ - EEL_CHECK_STRING_RESULT (check_compute_stretch (0, 0, 16, 0, 0, 0, 0), "0,0:16"); - EEL_CHECK_STRING_RESULT (check_compute_stretch (0, 0, 16, 16, 16, 17, 17), "0,0:17"); - EEL_CHECK_STRING_RESULT (check_compute_stretch (0, 0, 16, 16, 16, 17, 16), "0,0:16"); - EEL_CHECK_STRING_RESULT (check_compute_stretch (100, 100, 64, 105, 105, 40, 40), "35,35:129"); -} - gboolean nemo_icon_container_is_layout_rtl (NemoIconContainer *container) { @@ -8095,9 +8111,15 @@ nemo_icon_container_icon_get_bounding_box (NemoIconContainer *container, void nemo_icon_container_update_icon (NemoIconContainer *container, - NemoIcon *icon) + NemoIcon *icon) { - NEMO_ICON_CONTAINER_GET_CLASS (container)->update_icon (container, icon); + gboolean ok = FALSE; + + if (icon != NULL) { + ok = icon->ok_to_show_thumb; + } + + NEMO_ICON_CONTAINER_GET_CLASS (container)->update_icon (container, icon, ok); } gint @@ -8106,4 +8128,49 @@ nemo_icon_container_get_additional_text_line_count (NemoIconContainer *container return NEMO_ICON_CONTAINER_GET_CLASS (container)->get_additional_text_line_count (container); } +void +nemo_icon_container_set_ok_to_load_thumbs (NemoIconContainer *container, + gboolean ok) +{ + container->details->ok_to_load_thumbs = ok; + + if (ok) { + queue_update_visible_icons (container, INITIAL_UPDATE_VISIBLE_DELAY); + } +} + +#if ! defined (NEMO_OMIT_SELF_CHECK) + +static char * +check_compute_stretch (int icon_x, int icon_y, int icon_size, + int start_pointer_x, int start_pointer_y, + int end_pointer_x, int end_pointer_y) +{ + StretchState start, current; + + start.icon_x = icon_x; + start.icon_y = icon_y; + start.icon_size = icon_size; + start.pointer_x = start_pointer_x; + start.pointer_y = start_pointer_y; + current.pointer_x = end_pointer_x; + current.pointer_y = end_pointer_y; + + compute_stretch (&start, ¤t); + + return g_strdup_printf ("%d,%d:%d", + current.icon_x, + current.icon_y, + current.icon_size); +} + +void +nemo_self_check_icon_container (void) +{ + EEL_CHECK_STRING_RESULT (check_compute_stretch (0, 0, 16, 0, 0, 0, 0), "0,0:16"); + EEL_CHECK_STRING_RESULT (check_compute_stretch (0, 0, 16, 16, 16, 17, 17), "0,0:17"); + EEL_CHECK_STRING_RESULT (check_compute_stretch (0, 0, 16, 16, 16, 17, 16), "0,0:16"); + EEL_CHECK_STRING_RESULT (check_compute_stretch (100, 100, 64, 105, 105, 40, 40), "35,35:129"); +} + #endif /* ! NEMO_OMIT_SELF_CHECK */ diff --git a/libnemo-private/nemo-icon-container.h b/libnemo-private/nemo-icon-container.h index 9e76d5810..c76242e97 100644 --- a/libnemo-private/nemo-icon-container.h +++ b/libnemo-private/nemo-icon-container.h @@ -142,7 +142,8 @@ typedef struct { NemoIconData *data, int icon_size, gboolean for_drag_accept, - gboolean *has_window_open); + gboolean *has_window_open, + gboolean visible); void (* get_icon_text) (NemoIconContainer *container, NemoIconData *data, char **editable_text, @@ -150,7 +151,8 @@ typedef struct { gboolean *pinned, gboolean include_invisible); void (* update_icon) (NemoIconContainer *container, - NemoIcon *icon); + NemoIcon *icon, + gboolean visible); char * (* get_icon_description) (NemoIconContainer *container, NemoIconData *data); int (* compare_icons) (NemoIconContainer *container, @@ -373,4 +375,6 @@ void nemo_icon_container_setup_tooltip_preference_callback (NemoIconCont void nemo_icon_container_update_tooltip_text (NemoIconContainer *container, NemoIconCanvasItem *item); gint nemo_icon_container_get_additional_text_line_count (NemoIconContainer *container); +void nemo_icon_container_set_ok_to_load_thumbs (NemoIconContainer *container, + gboolean ok); #endif /* NEMO_ICON_CONTAINER_H */ diff --git a/libnemo-private/nemo-icon-private.h b/libnemo-private/nemo-icon-private.h index 6fad0e6b6..54fe37a6a 100644 --- a/libnemo-private/nemo-icon-private.h +++ b/libnemo-private/nemo-icon-private.h @@ -300,6 +300,12 @@ struct NemoIconContainerDetails { eel_boolean_bit store_layout_timestamps : 1; eel_boolean_bit store_layout_timestamps_when_finishing_new_icons : 1; + gint ok_to_load_thumbs; + guint update_visible_icons_id; + + GQueue *lazy_icon_load_queue; + guint lazy_icon_load_id; + GList *current_selection; gint current_selection_count; gint fixed_text_height; @@ -383,7 +389,8 @@ NemoIconInfo *nemo_icon_container_get_icon_images (NemoIconContainer *container, NemoIconData *data, int size, gboolean for_drag_accept, - gboolean *has_open_window); + gboolean *has_open_window, + gboolean visible); void nemo_icon_container_get_icon_text (NemoIconContainer *container, NemoIconData *data, char **editable_text, diff --git a/libnemo-private/nemo-icon.h b/libnemo-private/nemo-icon.h index 93a8e2b18..583090b66 100644 --- a/libnemo-private/nemo-icon.h +++ b/libnemo-private/nemo-icon.h @@ -70,6 +70,8 @@ typedef struct { eel_boolean_bit is_visible : 1; eel_boolean_bit has_lazy_position : 1; + + eel_boolean_bit ok_to_show_thumb : 1; } NemoIcon; #endif /* NEMO_ICON_CONTAINER_PRIVATE_H */ diff --git a/libnemo-private/nemo-thumbnails.c b/libnemo-private/nemo-thumbnails.c index 60180456d..6f63e20f7 100644 --- a/libnemo-private/nemo-thumbnails.c +++ b/libnemo-private/nemo-thumbnails.c @@ -423,6 +423,7 @@ thumbnail_thread_notify_file_changed (gpointer image_uri) nemo_file_unref (file); } g_free (image_uri); + // g_printerr ("length: %d REMOVE\n" , g_hash_table_size (thumbnails_to_make_hash)); return FALSE; } @@ -618,7 +619,9 @@ thumbnail_thread_starter_cb (gpointer data) } void -nemo_create_thumbnail (NemoFile *file, gint throttle_count) +nemo_create_thumbnail (NemoFile *file, + gint throttle_count, + gboolean prioritize) { time_t file_mtime = 0; NemoThumbnailInfo *info; @@ -658,7 +661,7 @@ nemo_create_thumbnail (NemoFile *file, gint throttle_count) thumbnails_to_make_hash = g_hash_table_new (g_str_hash, g_str_equal); } - + // g_printerr ("length: %d ADD\n" , g_hash_table_size (thumbnails_to_make_hash)); /* Check if it is already in the list of thumbnails to make. */ existing = g_hash_table_lookup (thumbnails_to_make_hash, info->image_uri); @@ -670,8 +673,14 @@ nemo_create_thumbnail (NemoFile *file, gint throttle_count) info->image_uri); } - g_queue_push_tail ((GQueue *)&thumbnails_to_make, info); - node = g_queue_peek_tail_link ((GQueue *)&thumbnails_to_make); + if (prioritize) { + g_queue_push_head ((GQueue *)&thumbnails_to_make, info); + node = g_queue_peek_head_link ((GQueue *)&thumbnails_to_make); + } else { + g_queue_push_tail ((GQueue *)&thumbnails_to_make, info); + node = g_queue_peek_tail_link ((GQueue *)&thumbnails_to_make); + } + g_hash_table_insert (thumbnails_to_make_hash, info->image_uri, node); @@ -697,7 +706,12 @@ nemo_create_thumbnail (NemoFile *file, gint throttle_count) existing_info = existing->data; existing_info->original_file_mtime = info->original_file_mtime; free_thumbnail_info (info); - } + + if (existing && existing->data != currently_thumbnailing) { + g_queue_unlink ((GQueue *)&thumbnails_to_make, existing); + g_queue_push_head_link ((GQueue *)&thumbnails_to_make, existing); + } + } /********************************* * MUTEX UNLOCKED diff --git a/libnemo-private/nemo-thumbnails.h b/libnemo-private/nemo-thumbnails.h index 8e50dfe21..5c70306b7 100644 --- a/libnemo-private/nemo-thumbnails.h +++ b/libnemo-private/nemo-thumbnails.h @@ -32,7 +32,7 @@ #define THUMBNAIL_CREATION_DELAY_SECS 3 /* Returns NULL if there's no thumbnail yet. */ -void nemo_create_thumbnail (NemoFile *file, gint throttle_count); +void nemo_create_thumbnail (NemoFile *file, gint throttle_count, gboolean prioritize); gboolean nemo_can_thumbnail (NemoFile *file); gboolean nemo_can_thumbnail_internally (NemoFile *file); gboolean nemo_thumbnail_is_mimetype_limited_by_size diff --git a/src/nemo-icon-view-container.c b/src/nemo-icon-view-container.c index 3f72fabcf..ad0f5bf09 100644 --- a/src/nemo-icon-view-container.c +++ b/src/nemo-icon-view-container.c @@ -85,7 +85,8 @@ nemo_icon_view_container_get_icon_images (NemoIconContainer *container, NemoIconData *data, int size, gboolean for_drag_accept, - gboolean *has_window_open) + gboolean *has_window_open, + gboolean visible) { NemoIconView *icon_view; NemoFile *file; @@ -106,9 +107,12 @@ nemo_icon_view_container_get_icon_images (NemoIconContainer *container, *has_window_open = nemo_file_has_open_window (file); flags = NEMO_FILE_ICON_FLAGS_USE_MOUNT_ICON_AS_EMBLEM | - NEMO_FILE_ICON_FLAGS_USE_THUMBNAILS | NEMO_FILE_ICON_FLAGS_FORCE_THUMBNAIL_SIZE; + if (visible) { + flags |= NEMO_FILE_ICON_FLAGS_USE_THUMBNAILS; + } + if (for_drag_accept) { flags |= NEMO_FILE_ICON_FLAGS_FOR_DRAG_ACCEPT; } @@ -117,7 +121,7 @@ nemo_icon_view_container_get_icon_images (NemoIconContainer *container, nemo_view_get_directory_as_file (NEMO_VIEW (icon_view))); scale = gtk_widget_get_scale_factor (GTK_WIDGET (icon_view)); - icon_info = nemo_file_get_icon (file, size, 0, scale, flags); + icon_info = nemo_file_get_icon (file, size, size, scale, flags); /* apply emblems */ if (emblem_icons != NULL) { @@ -198,17 +202,14 @@ nemo_icon_view_container_prioritize_thumbnailing (NemoIconContainer *container, NemoIconData *data) { NemoFile *file; - char *uri; file = (NemoFile *) data; g_assert (NEMO_IS_FILE (file)); - if (nemo_file_is_thumbnailing (file)) { - uri = nemo_file_get_uri (file); - nemo_thumbnail_prioritize (uri); - g_free (uri); - } + if (nemo_can_thumbnail (file) && !nemo_file_has_loaded_thumbnail (file)) { + nemo_create_thumbnail (file, 0, TRUE); + } } static void @@ -1463,7 +1464,8 @@ icon_get_size (NemoIconContainer *container, static void nemo_icon_view_container_update_icon (NemoIconContainer *container, - NemoIcon *icon) + NemoIcon *icon, + gboolean visible) { NemoIconContainerDetails *details; guint icon_size; @@ -1501,7 +1503,8 @@ nemo_icon_view_container_update_icon (NemoIconContainer *container, icon->data, icon_size, icon == details->drop_target, - &has_open_window); + &has_open_window, + visible); if (container->details->forced_icon_size > 0) { gint scale_factor; diff --git a/src/nemo-icon-view-grid-container.c b/src/nemo-icon-view-grid-container.c index cf551df1a..0deea3a37 100644 --- a/src/nemo-icon-view-grid-container.c +++ b/src/nemo-icon-view-grid-container.c @@ -64,7 +64,8 @@ nemo_icon_view_grid_container_get_icon_images (NemoIconContainer *container, NemoIconData *data, int size, gboolean for_drag_accept, - gboolean *has_window_open) + gboolean *has_window_open, + gboolean visible) { NemoIconView *icon_view; NemoFile *file; @@ -848,7 +849,8 @@ nemo_icon_view_grid_container_move_icon (NemoIconContainer *container, static void nemo_icon_view_grid_container_update_icon (NemoIconContainer *container, - NemoIcon *icon) + NemoIcon *icon, + gboolean visible) { NemoIconContainerDetails *details; guint icon_size; @@ -876,7 +878,7 @@ nemo_icon_view_grid_container_update_icon (NemoIconContainer *container, /* Get the icons. */ icon_info = nemo_icon_container_get_icon_images (container, icon->data, icon_size, icon == details->drop_target, - &has_open_window); + &has_open_window, TRUE); scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (container)); pixbuf = nemo_icon_info_get_desktop_pixbuf_at_size (icon_info, diff --git a/src/nemo-icon-view.c b/src/nemo-icon-view.c index 813f62d87..edbd610d8 100644 --- a/src/nemo-icon-view.c +++ b/src/nemo-icon-view.c @@ -985,6 +985,8 @@ nemo_icon_view_begin_loading (NemoView *view) uri = nemo_file_get_uri (file); icon_container = GTK_WIDGET (get_icon_container (icon_view)); + nemo_icon_container_set_ok_to_load_thumbs (NEMO_ICON_CONTAINER (icon_container), FALSE); + nemo_icon_container_begin_loading (NEMO_ICON_CONTAINER (icon_container)); nemo_icon_container_set_allow_moves (NEMO_ICON_CONTAINER (icon_container), @@ -1090,7 +1092,7 @@ nemo_icon_view_end_loading (NemoView *view, monitor = nemo_clipboard_monitor_get (); info = nemo_clipboard_monitor_get_clipboard_info (monitor); - + nemo_icon_container_set_ok_to_load_thumbs (NEMO_ICON_CONTAINER (icon_container), TRUE); icon_view_notify_clipboard_info (monitor, info, icon_view); } diff --git a/src/nemo-list-model.c b/src/nemo-list-model.c index f18e4cf8e..20446d7aa 100644 --- a/src/nemo-list-model.c +++ b/src/nemo-list-model.c @@ -100,6 +100,7 @@ struct FileEntry { GSequenceIter *ptr; guint loaded : 1; guint expanding : 1; + guint ok_to_show_thumb : 1; }; G_DEFINE_TYPE_WITH_CODE (NemoListModel, nemo_list_model, G_TYPE_OBJECT, @@ -314,9 +315,13 @@ nemo_list_model_get_value (GtkTreeModel *tree_model, GtkTreeIter *iter, int colu icon_size = nemo_get_list_icon_size_for_zoom_level (zoom_level); icon_scale = nemo_list_model_get_icon_scale (model); - flags = NEMO_FILE_ICON_FLAGS_USE_THUMBNAILS | - NEMO_FILE_ICON_FLAGS_FORCE_THUMBNAIL_SIZE | + flags = NEMO_FILE_ICON_FLAGS_FORCE_THUMBNAIL_SIZE | NEMO_FILE_ICON_FLAGS_USE_MOUNT_ICON_AS_EMBLEM; + + if (file_entry->ok_to_show_thumb) { + flags |= NEMO_FILE_ICON_FLAGS_USE_THUMBNAILS; + } + if (model->details->drag_view != NULL) { GtkTreePath *path_a, *path_b; @@ -415,6 +420,12 @@ nemo_list_model_get_value (GtkTreeModel *tree_model, GtkTreeIter *iter, int colu } break; + case NEMO_LIST_MODEL_ICON_SHOWN: + g_value_init (value, G_TYPE_BOOLEAN); + + g_value_set_boolean (value, file_entry->ok_to_show_thumb); + file_entry->ok_to_show_thumb = TRUE; + break; default: if (column >= NEMO_LIST_MODEL_NUM_COLUMNS || column < (int)(NEMO_LIST_MODEL_NUM_COLUMNS + model->details->columns->len)) { NemoColumn *nemo_column; @@ -1728,6 +1739,8 @@ nemo_list_model_subdirectory_done_loading (NemoListModel *model, NemoDirectory * return; } + + file_entry = g_sequence_get (parent_ptr); files = file_entry->files; @@ -1829,4 +1842,4 @@ nemo_list_model_set_expanding (NemoListModel *model, NemoDirectory *directory) entry = g_sequence_get (ptr); entry->expanding = TRUE; -} \ No newline at end of file +} diff --git a/src/nemo-list-model.h b/src/nemo-list-model.h index 797a9f113..30a5b0abd 100644 --- a/src/nemo-list-model.h +++ b/src/nemo-list-model.h @@ -55,6 +55,7 @@ enum { NEMO_LIST_MODEL_LARGEST_ICON_COLUMN, NEMO_LIST_MODEL_FILE_NAME_IS_EDITABLE_COLUMN, NEMO_LIST_MODEL_TEXT_WEIGHT_COLUMN, + NEMO_LIST_MODEL_ICON_SHOWN, NEMO_LIST_MODEL_NUM_COLUMNS }; diff --git a/src/nemo-list-view.c b/src/nemo-list-view.c index e16ff2726..8002be103 100644 --- a/src/nemo-list-view.c +++ b/src/nemo-list-view.c @@ -57,6 +57,7 @@ #include #include #include +#include #include #include @@ -91,6 +92,9 @@ struct NemoListViewDetails { int drag_x; int drag_y; + gint ok_to_load_thumbs; + guint update_visible_icons_id; + gboolean rename_on_release; gboolean drag_started; gboolean ignore_button_release; @@ -140,6 +144,9 @@ struct SelectionForeachData { /* Wait for the rename to end when activating a file being renamed */ #define WAIT_FOR_RENAME_ON_ACTIVATE 200 +#define INITIAL_UPDATE_VISIBLE_DELAY 300 +#define NORMAL_UPDATE_VISIBLE_DELAY 50 + static GdkCursor * hand_cursor = NULL; static GtkTargetList * source_target_list = NULL; @@ -172,9 +179,14 @@ static char **get_column_order (NemoListView * static char **get_default_column_order (NemoListView *list_view); static void set_columns_settings_from_metadata_and_preferences (NemoListView *list_view); +static void queue_update_visible_icons (NemoListView *view, gint delay); +static NemoZoomLevel nemo_list_view_get_zoom_level (NemoView *view); +static void prioritize_visible_files (NemoListView *view); G_DEFINE_TYPE (NemoListView, nemo_list_view, NEMO_TYPE_VIEW); +static gint click_policy = NEMO_CLICK_POLICY_SINGLE; + static const char * default_trash_visible_columns[] = { "name", "size", "type", "trashed_on", "trash_orig_path", NULL }; @@ -379,13 +391,6 @@ button_event_modifies_selection (GdkEventButton *event) return (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) != 0; } -static int -get_click_policy (void) -{ - return g_settings_get_enum (nemo_preferences, - NEMO_PREFERENCES_CLICK_POLICY); -} - static void nemo_list_view_did_not_drag (NemoListView *view, GdkEventButton *event) @@ -411,7 +416,7 @@ nemo_list_view_did_not_drag (NemoListView *view, } } - if ((get_click_policy () == NEMO_CLICK_POLICY_SINGLE) + if ((click_policy == NEMO_CLICK_POLICY_SINGLE) && !button_event_modifies_selection(event)) { if (event->button == 1) { activate_selected_items (view); @@ -595,7 +600,7 @@ motion_notify_callback (GtkWidget *widget, return GDK_EVENT_PROPAGATE; } - if (get_click_policy () == NEMO_CLICK_POLICY_SINGLE) { + if (click_policy == NEMO_CLICK_POLICY_SINGLE) { GtkTreePath *old_hover_path; old_hover_path = view->details->hover_path; @@ -738,7 +743,7 @@ leave_notify_callback (GtkWidget *widget, view = NEMO_LIST_VIEW (callback_data); - if (get_click_policy () == NEMO_CLICK_POLICY_SINGLE && + if (click_policy == NEMO_CLICK_POLICY_SINGLE && view->details->hover_path != NULL) { gtk_tree_path_free (view->details->hover_path); view->details->hover_path = NULL; @@ -756,7 +761,7 @@ enter_notify_callback (GtkWidget *widget, view = NEMO_LIST_VIEW (callback_data); - if (get_click_policy () == NEMO_CLICK_POLICY_SINGLE) { + if (click_policy == NEMO_CLICK_POLICY_SINGLE) { if (view->details->hover_path != NULL) { gtk_tree_path_free (view->details->hover_path); } @@ -967,7 +972,7 @@ static gboolean handle_icon_double_click (NemoListView *view, GtkTreePath *path, GdkEventButton *event, gboolean on_expander) { /* Ignore double click if we are in single click mode */ - if (get_click_policy () == NEMO_CLICK_POLICY_SINGLE) { + if (click_policy == NEMO_CLICK_POLICY_SINGLE) { return FALSE; } @@ -1258,6 +1263,8 @@ static void subdirectory_done_loading_callback (NemoDirectory *directory, NemoListView *view) { nemo_list_model_subdirectory_done_loading (view->details->model, directory); + + queue_update_visible_icons (view, INITIAL_UPDATE_VISIBLE_DELAY); } static void @@ -1476,6 +1483,17 @@ key_press_callback (GtkWidget *widget, GdkEventKey *event, gpointer callback_dat return handled; } +static void +set_ok_to_load_thumbs (NemoListView *list_view, + gboolean ok) +{ + list_view->details->ok_to_load_thumbs = ok; + + if (ok) { + queue_update_visible_icons (list_view, INITIAL_UPDATE_VISIBLE_DELAY); + } +} + static void nemo_list_view_reveal_selection (NemoView *view) { @@ -2134,7 +2152,7 @@ filename_cell_data_func (GtkTreeViewColumn *column, NEMO_LIST_MODEL_TEXT_WEIGHT_COLUMN, &weight, -1); - if (get_click_policy () == NEMO_CLICK_POLICY_SINGLE) { + if (click_policy == NEMO_CLICK_POLICY_SINGLE) { path = gtk_tree_model_get_path (model, iter); if (view->details->hover_path == NULL || @@ -2171,6 +2189,96 @@ focus_in_event_callback (GtkWidget *widget, GdkEventFocus *event, gpointer user_ return FALSE; } +static void +prioritize_visible_files (NemoListView *view) +{ + NemoFile *last_file; + // GList *queue_list, *l; + GdkRectangle vrect; + GtkTreeIter iter; + GtkTreePath *path; + gint icon_size, cy, start_y, end_y, stepdown; + gint bin_y; + + gtk_tree_view_get_visible_rect (view->details->tree_view, + &vrect); + icon_size = nemo_get_list_icon_size_for_zoom_level (nemo_list_view_get_zoom_level (NEMO_VIEW (view))); + + gtk_tree_view_convert_tree_to_bin_window_coords(view->details->tree_view, + 1, vrect.y, + NULL, &bin_y); + + stepdown = icon_size * .75; + + start_y = bin_y - (vrect.height / 2); + end_y = bin_y + vrect.height + (vrect.height / 2); + + last_file = NULL; + cy = end_y; + + // Images that start out un-thumbnailed end up resolving in reverse + // order, so work bottom-up here. + while (cy > start_y) { + if (gtk_tree_view_get_path_at_pos (view->details->tree_view, + 1, cy, + &path, NULL, NULL, NULL)) { + NemoFile *file; + gboolean shown; + + gtk_tree_model_get_iter (GTK_TREE_MODEL (view->details->model), + &iter, path); + + gtk_tree_path_free (path); + gtk_tree_model_get (GTK_TREE_MODEL (view->details->model), + &iter, + NEMO_LIST_MODEL_ICON_SHOWN, &shown, + NEMO_LIST_MODEL_FILE_COLUMN, &file, -1); + + /* We'll catch some files twice, so filter them out */ + if (file != NULL && file != last_file) { + last_file = file; + + nemo_file_set_load_thumb (file, TRUE); + + if (nemo_file_is_thumbnailing (file)) { + nemo_thumbnail_prioritize (nemo_file_peek_uri (file)); + } else { + nemo_file_invalidate_attributes (file, NEMO_FILE_ATTRIBUTES_FOR_ICON); + } + } + } + + cy -= stepdown; + } +} + +static gboolean +update_visible_icons_cb (NemoListView *view) +{ + prioritize_visible_files (view); + + view->details->update_visible_icons_id = 0; + return G_SOURCE_REMOVE; +} + +static void +queue_update_visible_icons(NemoListView *view, + gint delay) +{ + if (view->details->update_visible_icons_id > 0) { + g_source_remove (view->details->update_visible_icons_id); + } + + view->details->update_visible_icons_id = g_timeout_add (delay, (GSourceFunc) update_visible_icons_cb, view); +} + +static void +handle_vadjustment_changed (GtkAdjustment *adjustment, + NemoListView *view) +{ + queue_update_visible_icons (view, NORMAL_UPDATE_VISIBLE_DELAY); +} + static gint get_icon_scale_callback (NemoListModel *model, NemoListView *view) @@ -2178,6 +2286,20 @@ get_icon_scale_callback (NemoListModel *model, return gtk_widget_get_scale_factor (GTK_WIDGET (view->details->tree_view)); } +static void +on_treeview_realized (GtkWidget *widget, + gpointer user_data) +{ + NemoListView *view = NEMO_LIST_VIEW (user_data); + GtkAdjustment *adjust; + + adjust = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (view->details->tree_view)); + g_signal_connect (adjust, + "value-changed", + G_CALLBACK (handle_vadjustment_changed), + view); +} + static void create_and_set_up_tree_view (NemoListView *view) { @@ -2263,6 +2385,8 @@ create_and_set_up_tree_view (NemoListView *view) g_signal_connect_object (view->details->tree_view, "focus_in_event", G_CALLBACK(focus_in_event_callback), view, 0); + g_signal_connect (view->details->tree_view, "realize", G_CALLBACK (on_treeview_realized), view); + view->details->model = g_object_new (NEMO_TYPE_LIST_MODEL, NULL); gtk_tree_view_set_model (view->details->tree_view, GTK_TREE_MODEL (view->details->model)); /* Need the model for the dnd drop icon "accept" change */ @@ -2435,6 +2559,7 @@ nemo_list_view_add_file (NemoView *view, NemoFile *file, NemoDirectory *director model = NEMO_LIST_VIEW (view)->details->model; nemo_list_model_add_file (model, file, directory); + queue_update_visible_icons (NEMO_LIST_VIEW (view), INITIAL_UPDATE_VISIBLE_DELAY); } static char ** @@ -2676,6 +2801,8 @@ nemo_list_view_begin_loading (NemoView *view) set_zoom_level_from_metadata_and_preferences (list_view); set_columns_settings_from_metadata_and_preferences (list_view); + set_ok_to_load_thumbs (list_view, FALSE); + AtkObject *atk = gtk_widget_get_accessible (GTK_WIDGET (NEMO_LIST_VIEW (view)->details->tree_view)); g_signal_connect_object (atk, "column-reordered", @@ -2706,6 +2833,8 @@ nemo_list_view_clear (NemoView *view) list_view = NEMO_LIST_VIEW (view); + list_view->details->ok_to_load_thumbs = FALSE; + tree_selection = gtk_tree_view_get_selection (list_view->details->tree_view); g_signal_handlers_block_by_func (tree_selection, list_selection_changed_callback, view); @@ -3582,8 +3711,11 @@ nemo_list_view_click_policy_changed (NemoView *directory_view) view = NEMO_LIST_VIEW (directory_view); + click_policy = g_settings_get_enum (nemo_preferences, + NEMO_PREFERENCES_CLICK_POLICY); + /* ensure that we unset the hand cursor and refresh underlined rows */ - if (get_click_policy () == NEMO_CLICK_POLICY_DOUBLE) { + if (click_policy == NEMO_CLICK_POLICY_DOUBLE) { if (view->details->hover_path != NULL) { if (gtk_tree_model_get_iter (GTK_TREE_MODEL (view->details->model), &iter, view->details->hover_path)) { @@ -3607,7 +3739,7 @@ nemo_list_view_click_policy_changed (NemoView *directory_view) } g_clear_object (&hand_cursor); - } else if (get_click_policy () == NEMO_CLICK_POLICY_SINGLE) { + } else if (click_policy == NEMO_CLICK_POLICY_SINGLE) { if (hand_cursor == NULL) { hand_cursor = gdk_cursor_new(GDK_HAND2); } @@ -3868,6 +4000,8 @@ nemo_list_view_end_loading (NemoView *view, nemo_list_view_update_selection (view); + set_ok_to_load_thumbs (NEMO_LIST_VIEW (view), TRUE); + monitor = nemo_clipboard_monitor_get (); info = nemo_clipboard_monitor_get_clipboard_info (monitor); diff --git a/src/nemo-view.c b/src/nemo-view.c index 880ecd79a..4897f75e5 100644 --- a/src/nemo-view.c +++ b/src/nemo-view.c @@ -96,13 +96,13 @@ #include /* Minimum starting update inverval */ -#define UPDATE_INTERVAL_MIN 50 +#define UPDATE_INTERVAL_MIN 200 /* Maximum update interval */ -#define UPDATE_INTERVAL_MAX 2050 +#define UPDATE_INTERVAL_MAX 2000 /* Amount of miliseconds the update interval is increased */ #define UPDATE_INTERVAL_INC 250 /* Interval at which the update interval is increased */ -#define UPDATE_INTERVAL_TIMEOUT_INTERVAL 250 +#define UPDATE_INTERVAL_TIMEOUT_INTERVAL 500 /* Milliseconds that have to pass without a change to reset the update interval */ #define UPDATE_INTERVAL_RESET 1000 @@ -111,7 +111,7 @@ #define DUPLICATE_HORIZONTAL_ICON_OFFSET 70 #define DUPLICATE_VERTICAL_ICON_OFFSET 30 -#define MAX_QUEUED_UPDATES 500 +#define MAX_QUEUED_UPDATES 250 #define NEMO_VIEW_MENU_PATH_OPEN_PLACEHOLDER "/MenuBar/File/Open Placeholder" #define NEMO_VIEW_MENU_PATH_APPLICATIONS_SUBMENU_PLACEHOLDER "/MenuBar/File/Open Placeholder/Open With/Applications Placeholder"