diff --git a/src/nautilus-grid-cell.c b/src/nautilus-grid-cell.c index 43386fdb8c11a8abc630f3d0bdf620cd687c9569..1c684c4f060ec650e4164a0accc0afe60123c1f9 100644 --- a/src/nautilus-grid-cell.c +++ b/src/nautilus-grid-cell.c @@ -7,8 +7,10 @@ #include "nautilus-grid-cell.h" #include "nautilus-global-preferences.h" +#include "nautilus-icon-info.h" #include "nautilus-tag-manager.h" #include "nautilus-thumbnails.h" +#include "nautilus-ui-utilities.h" struct _NautilusGridCell { @@ -31,21 +33,32 @@ G_DEFINE_TYPE (NautilusGridCell, nautilus_grid_cell, NAUTILUS_TYPE_VIEW_CELL) static void update_icon (NautilusGridCell *self) { - g_autoptr (NautilusViewItem) item = NULL; - NautilusFileIconFlags flags; + g_autoptr (NautilusViewItem) item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self)); + gboolean is_cut; + + g_return_if_fail (item != NULL); + + g_object_get (item, "is-cut", &is_cut, NULL); + + if (is_cut) + { + gtk_picture_set_paintable (GTK_PICTURE (self->icon), NULL); + gtk_widget_remove_css_class (self->icon, "hidden-file"); + gtk_widget_remove_css_class (self->icon, "thumbnail"); + + return; + } + g_autoptr (GdkPaintable) icon_paintable = NULL; - NautilusFile *file; + NautilusFile *file = nautilus_view_item_get_file (item); guint icon_size; - gint scale_factor; + gint scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (self)); + NautilusFileIconFlags flags = NAUTILUS_FILE_ICON_FLAGS_USE_THUMBNAILS; - item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self)); - g_return_if_fail (item != NULL); - file = nautilus_view_item_get_file (item); g_object_get (self, "icon-size", &icon_size, NULL); - scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (self)); - flags = NAUTILUS_FILE_ICON_FLAGS_USE_THUMBNAILS; icon_paintable = nautilus_file_get_icon_paintable (file, icon_size, scale_factor, flags); + gtk_picture_set_paintable (GTK_PICTURE (self->icon), icon_paintable); if (nautilus_file_has_thumbnail (file) && @@ -57,6 +70,15 @@ update_icon (NautilusGridCell *self) { gtk_widget_remove_css_class (self->icon, "thumbnail"); } + + if (nautilus_file_is_hidden_file (file)) + { + gtk_widget_add_css_class (self->icon, "hidden-file"); + } + else + { + gtk_widget_remove_css_class (self->icon, "hidden-file"); + } } static void @@ -159,26 +181,6 @@ on_icon_size_changed (NautilusGridCell *self) gtk_widget_queue_resize (GTK_WIDGET (self)); } -static void -on_item_is_cut_changed (NautilusGridCell *self) -{ - gboolean is_cut; - g_autoptr (NautilusViewItem) item = NULL; - - item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self)); - g_object_get (item, - "is-cut", &is_cut, - NULL); - if (is_cut) - { - gtk_widget_add_css_class (self->icon, "cut"); - } - else - { - gtk_widget_remove_css_class (self->icon, "cut"); - } -} - static gboolean on_label_query_tooltip (GtkWidget *widget, int x, @@ -394,6 +396,40 @@ nautilus_grid_cell_size_allocate (GtkWidget *widget, gtk_widget_size_allocate (self->labels_box, &child_allocation, baseline); } +static void +snapshot (GtkWidget *widget, + GtkSnapshot *snapshot) +{ + NautilusGridCell *self = NAUTILUS_GRID_CELL (widget); + g_autoptr (NautilusViewItem) item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self)); + gboolean is_cut; + + g_object_get (item, "is-cut", &is_cut, NULL); + + if (is_cut) + { + guint icon_size; + graphene_rect_t dash_bounds, icon_bounds; + GdkRGBA color; + gboolean is_light = !adw_style_manager_get_dark (adw_style_manager_get_default ()); + + g_object_get (self, "icon-size", &icon_size, NULL); + dash_bounds = GRAPHENE_RECT_INIT (EMBLEMS_BOX_WIDTH, 0, icon_size, icon_size); + graphene_rect_inset_r (&dash_bounds, 0.2 * icon_size, 0.2 * icon_size, &icon_bounds); + gtk_widget_get_color (widget, &color); + color.alpha = is_light ? 0.4 : 0.6; + + nautilus_ui_draw_icon_dashed_border (snapshot, &dash_bounds, color); + nautilus_ui_draw_symbolic_icon (snapshot, + "cut-large-symbolic", + &icon_bounds, + color, + gtk_widget_get_scale_factor (widget)); + } + + GTK_WIDGET_CLASS (nautilus_grid_cell_parent_class)->snapshot (widget, snapshot); +} + static void nautilus_grid_cell_class_init (NautilusGridCellClass *klass) { @@ -405,6 +441,8 @@ nautilus_grid_cell_class_init (NautilusGridCellClass *klass) widget_class->measure = nautilus_grid_cell_measure; widget_class->size_allocate = nautilus_grid_cell_size_allocate; + widget_class->snapshot = snapshot; + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/nautilus/ui/nautilus-grid-cell.ui"); gtk_widget_class_bind_template_child (widget_class, NautilusGridCell, icon); @@ -439,7 +477,9 @@ nautilus_grid_cell_init (NautilusGridCell *self) /* Connect automatically to an item. */ self->item_signal_group = g_signal_group_new (NAUTILUS_TYPE_VIEW_ITEM); g_signal_group_connect_swapped (self->item_signal_group, "notify::is-cut", - (GCallback) on_item_is_cut_changed, self); + (GCallback) update_icon, self); + g_signal_group_connect_swapped (self->item_signal_group, "notify::is-cut", + (GCallback) gtk_widget_queue_draw, self); g_signal_group_connect_swapped (self->item_signal_group, "file-changed", (GCallback) on_file_changed, self); g_signal_connect_object (self->item_signal_group, "bind", diff --git a/src/nautilus-name-cell.c b/src/nautilus-name-cell.c index 370d8a8883649d90f9677a68ecf5b4e88ea8ac9c..a661e0c7e8c05d1ed80e55a0cc6fe9ad75a18cff 100644 --- a/src/nautilus-name-cell.c +++ b/src/nautilus-name-cell.c @@ -7,8 +7,10 @@ #include "nautilus-name-cell.h" #include "nautilus-directory.h" +#include "nautilus-icon-info.h" #include "nautilus-file-utilities.h" #include "nautilus-thumbnails.h" +#include "nautilus-ui-utilities.h" #define SPINNER_DELAY_MS 200 @@ -130,31 +132,46 @@ update_labels (NautilusNameCell *self) static void update_icon (NautilusNameCell *self) { - NautilusFileIconFlags flags; - g_autoptr (GdkPaintable) icon_paintable = NULL; - g_autoptr (NautilusViewItem) item = NULL; - NautilusFile *file; + g_autoptr (NautilusViewItem) item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self)); guint icon_size; - gint scale_factor; - int icon_height; - int extra_margin; + gboolean is_cut; - item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self)); g_return_if_fail (item != NULL); - file = nautilus_view_item_get_file (item); g_object_get (self, "icon-size", &icon_size, NULL); - scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (self)); - flags = NAUTILUS_FILE_ICON_FLAGS_USE_THUMBNAILS; - - icon_paintable = nautilus_file_get_icon_paintable (file, icon_size, scale_factor, flags); - gtk_picture_set_paintable (GTK_PICTURE (self->icon), icon_paintable); + g_object_get (item, "is-cut", &is_cut, NULL); /* Set the same width for all icons regardless of aspect ratio. * Don't set the width here because it would get GtkPicture w4h confused. */ gtk_widget_set_size_request (self->fixed_height_box, icon_size, -1); + if (is_cut) + { + /* This is needed to retain a size for the icon so that we can know + * where to draw for cut items. */ + GtkSnapshot *snapshot = gtk_snapshot_new (); + g_autoptr (GdkPaintable) paintable = + gtk_snapshot_free_to_paintable (snapshot, + &GRAPHENE_SIZE_INIT (icon_size, icon_size)); + + gtk_picture_set_paintable (GTK_PICTURE (self->icon), paintable); + gtk_widget_remove_css_class (self->icon, "hidden-file"); + gtk_widget_remove_css_class (self->icon, "thumbnail"); + + return; + } + + NautilusFile *file = nautilus_view_item_get_file (item); + gint scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (self)); + NautilusFileIconFlags flags = NAUTILUS_FILE_ICON_FLAGS_USE_THUMBNAILS; + g_autoptr (GdkPaintable) icon_paintable = nautilus_file_get_icon_paintable (file, icon_size, + scale_factor, flags); + int icon_height; + int extra_margin; + + gtk_picture_set_paintable (GTK_PICTURE (self->icon), icon_paintable); + /* Give all items the same minimum width. This cannot be done by setting the * width request directly, as above, because it would get mess up with * height for width calculations. @@ -176,6 +193,15 @@ update_icon (NautilusNameCell *self) { gtk_widget_remove_css_class (self->icon, "thumbnail"); } + + if (nautilus_file_is_hidden_file (file)) + { + gtk_widget_add_css_class (self->icon, "hidden-file"); + } + else + { + gtk_widget_remove_css_class (self->icon, "hidden-file"); + } } static void @@ -234,6 +260,7 @@ on_icon_size_changed (NautilusNameCell *self) } update_icon (self); + gtk_widget_queue_draw (GTK_WIDGET (self)); } static void @@ -255,26 +282,6 @@ on_item_drag_accept_changed (NautilusNameCell *self) } } -static void -on_item_is_cut_changed (NautilusNameCell *self) -{ - gboolean is_cut; - g_autoptr (NautilusViewItem) item = NULL; - - item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self)); - g_object_get (item, - "is-cut", &is_cut, - NULL); - if (is_cut) - { - gtk_widget_add_css_class (self->icon, "cut"); - } - else - { - gtk_widget_remove_css_class (self->icon, "cut"); - } -} - static void on_loading_timeout (gpointer user_data) { @@ -381,7 +388,7 @@ nautilus_name_cell_init (NautilusNameCell *self) g_signal_group_connect_swapped (self->item_signal_group, "notify::drag-accept", (GCallback) on_item_drag_accept_changed, self); g_signal_group_connect_swapped (self->item_signal_group, "notify::is-cut", - (GCallback) on_item_is_cut_changed, self); + (GCallback) update_icon, self); g_signal_group_connect_swapped (self->item_signal_group, "notify::loading", (GCallback) on_item_is_loading_changed, self); g_signal_group_connect_swapped (self->item_signal_group, "file-changed", @@ -416,6 +423,60 @@ nautilus_name_cell_finalize (GObject *object) G_OBJECT_CLASS (nautilus_name_cell_parent_class)->finalize (object); } +static void +snapshot (GtkWidget *widget, + GtkSnapshot *snapshot) +{ + NautilusNameCell *self = NAUTILUS_NAME_CELL (widget); + g_autoptr (NautilusViewItem) item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self)); + gboolean is_cut; + + g_object_get (item, "is-cut", &is_cut, NULL); + + if (is_cut) + { + graphene_rect_t dash_bounds; + + if (gtk_widget_compute_bounds (self->icon, widget, &dash_bounds)) + { + guint icon_size; + GdkRGBA color; + gboolean is_light = !adw_style_manager_get_dark (adw_style_manager_get_default ()); + gboolean use_small_icon; + gchar *icon_name; + graphene_rect_t icon_bounds = dash_bounds; + + g_object_get (self, "icon-size", &icon_size, NULL); + gtk_widget_get_color (widget, &color); + color.alpha = is_light ? 0.4 : 0.6; + use_small_icon = icon_size <= NAUTILUS_LIST_ICON_SIZE_MEDIUM; + icon_name = use_small_icon ? "cut-symbolic" : "cut-large-symbolic"; + + if (icon_size >= NAUTILUS_THUMBNAIL_MINIMUM_ICON_SIZE) + { + nautilus_ui_draw_icon_dashed_border (snapshot, &dash_bounds, color); + + graphene_rect_inset_r (&dash_bounds, + 0.2 * dash_bounds.size.width, + 0.2 * dash_bounds.size.height, + &icon_bounds); + } + + nautilus_ui_draw_symbolic_icon (snapshot, + icon_name, + &icon_bounds, + color, + gtk_widget_get_scale_factor (widget)); + } + else + { + g_warning ("Could not compute icon bounds in cell coordinates."); + } + } + + GTK_WIDGET_CLASS (nautilus_name_cell_parent_class)->snapshot (widget, snapshot); +} + static void nautilus_name_cell_class_init (NautilusNameCellClass *klass) { @@ -425,6 +486,8 @@ nautilus_name_cell_class_init (NautilusNameCellClass *klass) object_class->dispose = nautilus_name_cell_dispose; object_class->finalize = nautilus_name_cell_finalize; + widget_class->snapshot = snapshot; + gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/nautilus/ui/nautilus-name-cell.ui"); diff --git a/src/nautilus-ui-utilities.c b/src/nautilus-ui-utilities.c index a762d98fd6398878442936eaeb02e6f4df426535..faa7caea1d11a9a4574fb77b9eba5cd2b0d6ea07 100644 --- a/src/nautilus-ui-utilities.c +++ b/src/nautilus-ui-utilities.c @@ -526,3 +526,226 @@ show_unmount_progress_aborted_cb (GMountOperation *op, { notify_unmount_done (op, NULL, user_data); } + +static float +get_dash_width (guint size) +{ + switch (size) + { + case NAUTILUS_LIST_ICON_SIZE_SMALL: + { + /* We don't want to draw borders for the smallest size. */ + g_assert_not_reached (); + } + + case NAUTILUS_LIST_ICON_SIZE_MEDIUM: + { + return 1.5; + } + + case NAUTILUS_GRID_ICON_SIZE_SMALL: + { + return 2.0; + } + + /* case NAUTILUS_LIST_ICON_SIZE_LARGE */ + case NAUTILUS_GRID_ICON_SIZE_SMALL_PLUS: + { + return 2.0; + } + + case NAUTILUS_GRID_ICON_SIZE_MEDIUM: + { + return 3.0; + } + + case NAUTILUS_GRID_ICON_SIZE_LARGE: + { + return 4.0; + } + + case NAUTILUS_GRID_ICON_SIZE_EXTRA_LARGE: + { + return 5.0; + } + + default: + { + g_assert_not_reached (); + } + break; + } +} + +static float +get_dash_length (guint size) +{ + switch (size) + { + case NAUTILUS_LIST_ICON_SIZE_SMALL: + { + /* We don't want to draw borders for the smallest size. */ + g_assert_not_reached (); + } + + case NAUTILUS_LIST_ICON_SIZE_MEDIUM: + { + return 6.0; + } + + case NAUTILUS_GRID_ICON_SIZE_SMALL: + { + return 10.0; + } + + /* case NAUTILUS_LIST_ICON_SIZE_LARGE */ + case NAUTILUS_GRID_ICON_SIZE_SMALL_PLUS: + { + return 10.0; + } + + case NAUTILUS_GRID_ICON_SIZE_MEDIUM: + { + return 15.0; + } + + case NAUTILUS_GRID_ICON_SIZE_LARGE: + { + return 20; + } + + case NAUTILUS_GRID_ICON_SIZE_EXTRA_LARGE: + { + return 25; + } + + default: + { + g_assert_not_reached (); + } + break; + } +} + +static float +get_dash_radius (guint size) +{ + switch (size) + { + case NAUTILUS_LIST_ICON_SIZE_SMALL: + { + /* We don't want to draw borders for the smallest size. */ + g_assert_not_reached (); + } + + case NAUTILUS_LIST_ICON_SIZE_MEDIUM: + { + return 4; + } + + case NAUTILUS_GRID_ICON_SIZE_SMALL: + { + return 5.33; + } + + /* case NAUTILUS_LIST_ICON_SIZE_LARGE */ + case NAUTILUS_GRID_ICON_SIZE_SMALL_PLUS: + { + return 5.33; + } + + case NAUTILUS_GRID_ICON_SIZE_MEDIUM: + { + return 8.0; + } + + case NAUTILUS_GRID_ICON_SIZE_LARGE: + { + return 10.66; + } + + case NAUTILUS_GRID_ICON_SIZE_EXTRA_LARGE: + { + return 13.33; + } + + default: + { + g_assert_not_reached (); + } + break; + } +} + +#define DASH_STROKE_FRACTION 13.0 / 20.0 + +void +nautilus_ui_draw_icon_dashed_border (GtkSnapshot *snapshot, + graphene_rect_t *rect, + GdkRGBA color) +{ + float width = rect->size.width; + float stroke_width = get_dash_width (width); + const float ideal_dash_length = get_dash_length (width) + stroke_width; + const float radius = get_dash_radius (width); + /* Need to inset the rectangle and the dash length by the dash width. */ + graphene_rect_t *dash_bounds = graphene_rect_inset (rect, + stroke_width / 2.0, + stroke_width / 2.0); + /* Calculate the path length and divide it by the number of dashes to have + * an exact fractional dash length with no overlap at the start/end point. + */ + float border_length = dash_bounds->size.width * 2 + + dash_bounds->size.height * 2 + + 2 * G_PI * radius - 8 * radius; + const float number_of_dashes = round (border_length / ideal_dash_length); + float dash_length = border_length / number_of_dashes; + float dash_pattern[] = + { + dash_length * DASH_STROKE_FRACTION, + dash_length * (1 - DASH_STROKE_FRACTION), + }; + graphene_size_t arc_size = GRAPHENE_SIZE_INIT (radius, radius); + GskRoundedRect round_rect; + GskPathBuilder *path_builder = gsk_path_builder_new (); + GskStroke *stroke = gsk_stroke_new (stroke_width); + + gsk_rounded_rect_init (&round_rect, dash_bounds, &arc_size, &arc_size, &arc_size, &arc_size); + + gsk_path_builder_add_rounded_rect (path_builder, &round_rect); + gsk_path_builder_close (path_builder); + + g_autoptr (GskPath) path = gsk_path_builder_free_to_path (path_builder); + + gsk_stroke_set_dash (stroke, dash_pattern, G_N_ELEMENTS (dash_pattern)); + gsk_stroke_set_line_cap (stroke, GSK_LINE_CAP_ROUND); + + gtk_snapshot_append_stroke (snapshot, path, stroke, &color); +} + +void +nautilus_ui_draw_symbolic_icon (GtkSnapshot *snapshot, + const gchar *icon_name, + const graphene_rect_t *rect, + GdkRGBA color, + int scale) +{ + g_autoptr (GIcon) gicon = g_themed_icon_new (icon_name); + g_autoptr (NautilusIconInfo) icon = nautilus_icon_info_lookup (gicon, + 2.0 * rect->size.width, + scale); + g_autoptr (GdkPaintable) paintable = nautilus_icon_info_get_paintable (icon); + const GdkRGBA colors[] = {color}; + + g_assert (GTK_IS_SYMBOLIC_PAINTABLE (paintable)); + + gtk_snapshot_save (snapshot); + gtk_snapshot_translate (snapshot, &rect->origin); + gtk_symbolic_paintable_snapshot_symbolic (GTK_SYMBOLIC_PAINTABLE (paintable), + snapshot, + rect->size.width, + rect->size.height, + colors, + G_N_ELEMENTS (colors)); + gtk_snapshot_restore (snapshot); +} diff --git a/src/nautilus-ui-utilities.h b/src/nautilus-ui-utilities.h index d4eb4f723411175aeb78b2bee3647c7005612454..a836107bab2fc178544ac4f7f1f4d590edd6d944 100644 --- a/src/nautilus-ui-utilities.h +++ b/src/nautilus-ui-utilities.h @@ -57,6 +57,14 @@ void nautilus_g_menu_replace_string_in_item (GMenu *menu, void nautilus_ui_frame_video (GtkSnapshot *snapshot, gdouble width, gdouble height); +void nautilus_ui_draw_icon_dashed_border (GtkSnapshot *snapshot, + graphene_rect_t *rect, + GdkRGBA color); +void nautilus_ui_draw_symbolic_icon (GtkSnapshot *snapshot, + const gchar *icon_name, + const graphene_rect_t *rect, + GdkRGBA color, + int scale); gboolean nautilus_date_time_is_between_dates (GDateTime *date, GDateTime *initial_date, diff --git a/src/resources/icons/cut-large-symbolic.svg b/src/resources/icons/cut-large-symbolic.svg new file mode 100644 index 0000000000000000000000000000000000000000..0609cabbb1db07e21875ae24dcd5a1283b4c7032 --- /dev/null +++ b/src/resources/icons/cut-large-symbolic.svg @@ -0,0 +1,2 @@ + + diff --git a/src/resources/icons/cut-symbolic.svg b/src/resources/icons/cut-symbolic.svg new file mode 100644 index 0000000000000000000000000000000000000000..eead6d84dad6e3bd3f5df8284e2318b977c8a4f8 --- /dev/null +++ b/src/resources/icons/cut-symbolic.svg @@ -0,0 +1,2 @@ + + diff --git a/src/resources/nautilus.gresource.xml.in b/src/resources/nautilus.gresource.xml.in index aa998df13f882cd1e4d220ed34583851e529aa4f..0eedfeefa028770f6719ec97d8b0fb96b0c4927b 100644 --- a/src/resources/nautilus.gresource.xml.in +++ b/src/resources/nautilus.gresource.xml.in @@ -57,5 +57,7 @@ icons/info-outline-symbolic.svg icons/nautilus-file-chooser-options-symbolic.svg icons/funnel-outline-symbolic.svg + icons/cut-symbolic.svg + icons/cut-large-symbolic.svg diff --git a/src/resources/style.css b/src/resources/style.css index d209cd9840ae42f86b4efafad647490e856b9001..121ba5ac9305ac15d78908a08566b41268793fdf 100644 --- a/src/resources/style.css +++ b/src/resources/style.css @@ -293,7 +293,7 @@ placesview list { background-color: color-mix(in srgb, currentColor 10%, transparent); } -.view .cut { +.view .hidden-file { opacity: 0.55; } diff --git a/src/resources/ui/nautilus-grid-cell.ui b/src/resources/ui/nautilus-grid-cell.ui index 0960fd15f90a6716099d97dccfdfda002d6a217c..f8d02831b2f9e8bcc8e1559b1e591360b005eca6 100644 --- a/src/resources/ui/nautilus-grid-cell.ui +++ b/src/resources/ui/nautilus-grid-cell.ui @@ -8,7 +8,7 @@ center center False - cover + scale-down