From a0d3bdc911ae955cf2bde5f194ba0120cff45cfa Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Sat, 8 Oct 2022 08:15:09 -0300 Subject: [PATCH] filechooserwidget: Move file icon to column view This commit moves the icon loading code into a new private widget called GtkFileThumbnail, which is bound to the GFileInfo of the model, and asynchronously loads the file icon from that. --- gtk/gtkfilechooserwidget.c | 145 +----------------- gtk/gtkfilethumbnail.c | 261 +++++++++++++++++++++++++++++++++ gtk/gtkfilethumbnail.h | 46 ++++++ gtk/meson.build | 1 + gtk/ui/gtkfilechooserwidget.ui | 14 +- 5 files changed, 320 insertions(+), 147 deletions(-) create mode 100644 gtk/gtkfilethumbnail.c create mode 100644 gtk/gtkfilethumbnail.h diff --git a/gtk/gtkfilechooserwidget.c b/gtk/gtkfilechooserwidget.c index 7714f2b7b9..711354a675 100644 --- a/gtk/gtkfilechooserwidget.c +++ b/gtk/gtkfilechooserwidget.c @@ -38,6 +38,7 @@ #include "gtkfilechooserutils.h" #include "gtkfilechooser.h" #include "gtkfilesystemmodel.h" +#include "gtkfilethumbnail.h" #include "gtkgrid.h" #include "gtkicontheme.h" #include "gtklabel.h" @@ -275,7 +276,6 @@ struct _GtkFileChooserWidget GFile *renamed_file; GtkTreeViewColumn *list_name_column; - GtkCellRenderer *list_pixbuf_renderer; GtkTreeViewColumn *list_time_column; GtkCellRenderer *list_date_renderer; GtkCellRenderer *list_time_renderer; @@ -363,7 +363,6 @@ enum { MODEL_COL_NAME_COLLATED, MODEL_COL_IS_FOLDER, MODEL_COL_IS_SENSITIVE, - MODEL_COL_ICON, MODEL_COL_SIZE_TEXT, MODEL_COL_DATE_TEXT, MODEL_COL_TIME_TEXT, @@ -383,7 +382,6 @@ enum { G_TYPE_STRING, /* MODEL_COL_NAME_COLLATED */ \ G_TYPE_BOOLEAN, /* MODEL_COL_IS_FOLDER */ \ G_TYPE_BOOLEAN, /* MODEL_COL_IS_SENSITIVE */ \ - G_TYPE_ICON, /* MODEL_COL_ICON */ \ G_TYPE_STRING, /* MODEL_COL_SIZE_TEXT */ \ G_TYPE_STRING, /* MODEL_COL_DATE_TEXT */ \ G_TYPE_STRING, /* MODEL_COL_TIME_TEXT */ \ @@ -2207,17 +2205,6 @@ file_list_query_tooltip_cb (GtkWidget *widget, return TRUE; } -static void -set_icon_cell_renderer_fixed_size (GtkFileChooserWidget *impl) -{ - int xpad, ypad; - - gtk_cell_renderer_get_padding (impl->list_pixbuf_renderer, &xpad, &ypad); - gtk_cell_renderer_set_fixed_size (impl->list_pixbuf_renderer, - xpad * 2 + ICON_SIZE, - ypad * 2 + ICON_SIZE); -} - static GtkWidget * get_accept_action_widget (GtkDialog *dialog, gboolean sensitive_only) @@ -3274,10 +3261,6 @@ gtk_file_chooser_widget_unroot (GtkWidget *widget) static void change_icon_theme (GtkFileChooserWidget *impl) { - /* the first cell in the first column is the icon column, and we have a fixed size there */ - set_icon_cell_renderer_fixed_size (impl); - - clear_model_cache (impl, MODEL_COL_ICON); gtk_widget_queue_resize (impl->browse_files_tree_view); } @@ -4346,53 +4329,6 @@ my_g_format_time_for_display (GtkFileChooserWidget *impl, return date_str; } -static void -copy_attribute (GFileInfo *to, - GFileInfo *from, - const char *attribute) -{ - GFileAttributeType type; - gpointer value; - - if (g_file_info_get_attribute_data (from, attribute, &type, &value, NULL)) - g_file_info_set_attribute (to, attribute, type, value); -} - -static void -file_system_model_got_thumbnail (GObject *object, - GAsyncResult *res, - gpointer data) -{ - GtkFileSystemModel *model = data; /* might be unreffed if operation was cancelled */ - GFile *file = G_FILE (object); - GFileInfo *queried, *info; - GtkTreeIter iter; - - queried = g_file_query_info_finish (file, res, NULL); - if (queried == NULL) - return; - - /* now we know model is valid */ - - /* file was deleted */ - if (!_gtk_file_system_model_get_iter_for_file (model, &iter, file)) - { - g_object_unref (queried); - return; - } - - info = g_file_info_dup (_gtk_file_system_model_get_info (model, &iter)); - - copy_attribute (info, queried, G_FILE_ATTRIBUTE_THUMBNAIL_PATH); - copy_attribute (info, queried, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED); - copy_attribute (info, queried, G_FILE_ATTRIBUTE_STANDARD_ICON); - - _gtk_file_system_model_update_file (model, file, info); - - g_object_unref (info); - g_object_unref (queried); -} - /* Copied from src/nautilus_file.c:get_description() */ struct { const char *icon_name; @@ -4542,71 +4478,6 @@ file_system_model_set (GtkFileSystemModel *model, else g_value_set_boolean (value, TRUE); break; - case MODEL_COL_ICON: - if (info) - { - if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_ICON)) - { - int scale; - GtkIconTheme *icon_theme; - - scale = gtk_widget_get_scale_factor (GTK_WIDGET (impl)); - icon_theme = gtk_icon_theme_get_for_display (gtk_widget_get_display (GTK_WIDGET (impl))); - - g_value_take_object (value, _gtk_file_info_get_icon (info, ICON_SIZE, scale, icon_theme)); - } - else - { - GtkTreeModel *tree_model; - GtkTreePath *start, *end; - GtkTreeIter iter; - gboolean visible; - - if (impl->browse_files_tree_view == NULL || - g_file_info_has_attribute (info, "filechooser::queried")) - return FALSE; - - tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (impl->browse_files_tree_view)); - if (tree_model != GTK_TREE_MODEL (model)) - return FALSE; - - if (!_gtk_file_system_model_get_iter_for_file (model, &iter, file)) - g_assert_not_reached (); - - if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (impl->browse_files_tree_view), &start, &end)) - { - GtkTreePath *path; - - gtk_tree_path_prev (start); - gtk_tree_path_next (end); - path = gtk_tree_model_get_path (tree_model, &iter); - visible = gtk_tree_path_compare (start, path) != 1 && - gtk_tree_path_compare (path, end) != 1; - gtk_tree_path_free (path); - gtk_tree_path_free (start); - gtk_tree_path_free (end); - } - else - visible = TRUE; - if (visible) - { - g_file_info_set_attribute_boolean (info, "filechooser::queried", TRUE); - g_file_query_info_async (file, - G_FILE_ATTRIBUTE_THUMBNAIL_PATH "," - G_FILE_ATTRIBUTE_THUMBNAILING_FAILED "," - G_FILE_ATTRIBUTE_STANDARD_ICON, - G_FILE_QUERY_INFO_NONE, - G_PRIORITY_DEFAULT, - _gtk_file_system_model_get_cancellable (model), - file_system_model_got_thumbnail, - model); - } - return FALSE; - } - } - else - g_value_set_boxed (value, NULL); - break; case MODEL_COL_SIZE: g_value_set_int64 (value, info ? g_file_info_get_size (info) : 0); break; @@ -7140,12 +7011,6 @@ path_bar_clicked (GtkPathBar *path_bar, static void update_cell_renderer_attributes (GtkFileChooserWidget *impl) { - gtk_tree_view_column_set_attributes (impl->list_name_column, - impl->list_pixbuf_renderer, - "gicon", MODEL_COL_ICON, - "sensitive", MODEL_COL_IS_SENSITIVE, - NULL); - gtk_tree_view_column_set_attributes (impl->list_size_column, impl->list_size_renderer, "text", MODEL_COL_SIZE_TEXT, @@ -7368,6 +7233,8 @@ gtk_file_chooser_widget_class_init (GtkFileChooserWidgetClass *class) widget_class->grab_focus = gtk_widget_grab_focus_child; widget_class->focus = gtk_widget_focus_child; + g_type_ensure (GTK_TYPE_FILE_THUMBNAIL); + /* * Signals */ @@ -7766,7 +7633,6 @@ gtk_file_chooser_widget_class_init (GtkFileChooserWidgetClass *class) gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, search_entry); gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, search_spinner); gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, list_name_column); - gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, list_pixbuf_renderer); gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, list_time_column); gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, list_date_renderer); gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, list_time_renderer); @@ -7888,11 +7754,6 @@ post_process_ui (GtkFileChooserWidget *impl) _gtk_path_bar_set_file (GTK_PATH_BAR (impl->browse_path_bar), file, FALSE); g_object_unref (file); - /* Set the fixed size icon renderer, this requires - * that impl->icon_size be already setup. - */ - set_icon_cell_renderer_fixed_size (impl); - gtk_popover_set_default_widget (GTK_POPOVER (impl->new_folder_popover), impl->new_folder_create_button); gtk_popover_set_default_widget (GTK_POPOVER (impl->rename_file_popover), impl->rename_file_rename_button); diff --git a/gtk/gtkfilethumbnail.c b/gtk/gtkfilethumbnail.c new file mode 100644 index 0000000000..633b6be912 --- /dev/null +++ b/gtk/gtkfilethumbnail.c @@ -0,0 +1,261 @@ +/* gtkfilethumbnail.c + * + * Copyright 2022 Georges Basile Stavracas Neto + * + * This file is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program. If not, see . + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#include "config.h" + +#include "gtkfilethumbnail.h" + +#include "gtkbinlayout.h" +#include "gtkfilechooserutils.h" +#include "gtkimage.h" +#include "gtkprivate.h" +#include "gtkwidget.h" + +#define ICON_SIZE 16 + +struct _GtkFileThumbnail +{ + GtkWidget parent; + + GtkWidget *image; + + GtkFileSystemItem *item; + GCancellable *cancellable; +}; + +typedef struct +{ + GtkWidgetClass parent; +} GtkFileThumbnailClass; + +G_DEFINE_FINAL_TYPE (GtkFileThumbnail, _gtk_file_thumbnail, GTK_TYPE_WIDGET) + +enum { + PROP_0, + PROP_ITEM, + N_PROPS, +}; + +static GParamSpec *properties [N_PROPS]; + +static void +copy_attribute (GFileInfo *to, + GFileInfo *from, + const char *attribute) +{ + GFileAttributeType type; + gpointer value; + + if (g_file_info_get_attribute_data (from, attribute, &type, &value, NULL)) + g_file_info_set_attribute (to, attribute, type, value); +} + +static gboolean +update_image (GtkFileThumbnail *self) +{ + GtkIconTheme *icon_theme; + GFileInfo *info; + GIcon *icon; + int scale; + + info = _gtk_file_system_item_get_file_info (self->item); + if (!g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_ICON)) + return FALSE; + + scale = gtk_widget_get_scale_factor (GTK_WIDGET (self)); + icon_theme = gtk_icon_theme_get_for_display (gtk_widget_get_display (GTK_WIDGET (self))); + + icon = _gtk_file_info_get_icon (info, ICON_SIZE, scale, icon_theme); + + gtk_image_set_from_gicon (GTK_IMAGE (self->image), icon); + + g_object_unref (icon); + + return TRUE; + +} + +static void +thumbnail_queried_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + GtkFileThumbnail *self = user_data; /* might be unreffed if operation was cancelled */ + GFile *file = G_FILE (object); + GFileInfo *queried; + GFileInfo *info; + + queried = g_file_query_info_finish (file, result, NULL); + if (queried == NULL) + return; + + info = _gtk_file_system_item_get_file_info (self->item); + copy_attribute (info, queried, G_FILE_ATTRIBUTE_THUMBNAIL_PATH); + copy_attribute (info, queried, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED); + copy_attribute (info, queried, G_FILE_ATTRIBUTE_STANDARD_ICON); + + update_image (self); + + g_clear_object (&queried); + + g_clear_object (&self->cancellable); +} + +static void +cancel_thumbnail (GtkFileThumbnail *self) +{ + g_cancellable_cancel (self->cancellable); + g_clear_object (&self->cancellable); +} + +static void +get_thumbnail (GtkFileThumbnail *self) +{ + if (!self->item) + return; + + if (!update_image (self)) + { + GFileInfo *info; + GFile *file; + + info = _gtk_file_system_item_get_file_info (self->item); + if (g_file_info_has_attribute (info, "filechooser::queried")) + return; + + g_assert (self->cancellable == NULL); + self->cancellable = g_cancellable_new (); + + file = _gtk_file_system_item_get_file (self->item); + g_file_info_set_attribute_boolean (info, "filechooser::queried", TRUE); + g_file_query_info_async (file, + G_FILE_ATTRIBUTE_THUMBNAIL_PATH "," + G_FILE_ATTRIBUTE_THUMBNAILING_FAILED "," + G_FILE_ATTRIBUTE_STANDARD_ICON, + G_FILE_QUERY_INFO_NONE, + G_PRIORITY_DEFAULT, + self->cancellable, + thumbnail_queried_cb, + self); + } +} + +static void +_gtk_file_thumbnail_dispose (GObject *object) +{ + GtkFileThumbnail *self = (GtkFileThumbnail *)object; + + _gtk_file_thumbnail_set_item (self, NULL); + + g_clear_pointer (&self->image, gtk_widget_unparent); + + G_OBJECT_CLASS (_gtk_file_thumbnail_parent_class)->dispose (object); +} + +static void +_gtk_file_thumbnail_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkFileThumbnail *self = GTK_FILE_THUMBNAIL (object); + + switch (prop_id) + { + case PROP_ITEM: + g_value_set_object (value, self->item); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +_gtk_file_thumbnail_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkFileThumbnail *self = GTK_FILE_THUMBNAIL (object); + + switch (prop_id) + { + case PROP_ITEM: + _gtk_file_thumbnail_set_item (self, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +_gtk_file_thumbnail_class_init (GtkFileThumbnailClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->dispose = _gtk_file_thumbnail_dispose; + object_class->get_property = _gtk_file_thumbnail_get_property; + object_class->set_property = _gtk_file_thumbnail_set_property; + + properties[PROP_ITEM] = + g_param_spec_object ("item", NULL, NULL, + GTK_TYPE_FILE_SYSTEM_ITEM, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPS, properties); + + gtk_widget_class_set_css_name (widget_class, I_("filethumbnail")); + + gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); +} + +static void +_gtk_file_thumbnail_init (GtkFileThumbnail *self) +{ + self->image = gtk_image_new (); + gtk_widget_set_parent (self->image, GTK_WIDGET (self)); +} + +GtkFileSystemItem * +_gtk_file_thumbnail_get_item (GtkFileThumbnail *self) +{ + g_assert (GTK_IS_FILE_THUMBNAIL (self)); + + return self->item; +} + +void +_gtk_file_thumbnail_set_item (GtkFileThumbnail *self, + GtkFileSystemItem *item) +{ + g_assert (GTK_IS_FILE_THUMBNAIL (self)); + g_assert (item == NULL || GTK_IS_FILE_SYSTEM_ITEM (item)); + + if (g_set_object (&self->item, item)) + { + cancel_thumbnail (self); + get_thumbnail (self); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ITEM]); + } +} + diff --git a/gtk/gtkfilethumbnail.h b/gtk/gtkfilethumbnail.h new file mode 100644 index 0000000000..12ffadefb0 --- /dev/null +++ b/gtk/gtkfilethumbnail.h @@ -0,0 +1,46 @@ +/* gtkfilethumbnail.h + * + * Copyright 2022 Georges Basile Stavracas Neto + * + * This file is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program. If not, see . + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + + +#ifndef __GTK_FILE_THUMBNAIL_H__ +#define __GTK_FILE_THUMBNAIL_H__ + +#include + +#include "gtkfilesystemmodel.h" + +G_BEGIN_DECLS + +#define GTK_TYPE_FILE_THUMBNAIL (_gtk_file_thumbnail_get_type ()) +#define GTK_FILE_THUMBNAIL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_FILE_THUMBNAIL, GtkFileThumbnail)) +#define GTK_IS_FILE_THUMBNAIL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_FILE_THUMBNAIL)) + +typedef struct _GtkFileThumbnail GtkFileThumbnail; + +GType _gtk_file_thumbnail_get_type (void) G_GNUC_CONST; + +GtkFileSystemItem *_gtk_file_thumbnail_get_item (GtkFileThumbnail *self); +void _gtk_file_thumbnail_set_item (GtkFileThumbnail *self, + GtkFileSystemItem *item); + +G_END_DECLS + +#endif /* __GTK_FILE_THUMBNAIL_H__ */ + diff --git a/gtk/meson.build b/gtk/meson.build index 5ce0ae81e3..db856250e4 100644 --- a/gtk/meson.build +++ b/gtk/meson.build @@ -109,6 +109,7 @@ gtk_private_sources = files([ 'gtkfilechoosernativeportal.c', 'gtkfilechooserutils.c', 'gtkfilesystemmodel.c', + 'gtkfilethumbnail.c', 'gtkgizmo.c', 'gtkiconcache.c', 'gtkiconcachevalidator.c', diff --git a/gtk/ui/gtkfilechooserwidget.ui b/gtk/ui/gtkfilechooserwidget.ui index 4e0090f9f7..09fb17af36 100644 --- a/gtk/ui/gtkfilechooserwidget.ui +++ b/gtk/ui/gtkfilechooserwidget.ui @@ -155,6 +155,15 @@