filechooserwidget: Add a grid view
authorGeorges Basile Stavracas Neto <georges.stavracas@gmail.com>
Sat, 22 Oct 2022 12:51:36 +0000 (09:51 -0300)
committerGeorges Basile Stavracas Neto <georges.stavracas@gmail.com>
Tue, 13 Dec 2022 17:45:15 +0000 (14:45 -0300)
Add a grid view outside of the widgetry tree. The grid view mimics
the column view using bindings, so we only need to manage the column
view.

Also add a button in the path bar section to toggle the view. This
is handled as a new 'toggle-view' action in the file chooser.

The way switching between views currently work is by setting either
the column or grid view as the child of the GtkScrolledWindow. This
has the benefit of unmapping the unused view, which is nice and can
avoid some tricky situations with thumbnails.

Fixes https://gitlab.gnome.org/GNOME/gtk/-/issues/233

gtk/gtkfilechooserwidget.c
gtk/ui/gtkfilechooserwidget.ui

index 29b3f4ba37af2b8e707e89b8056f714438d0a8a3..8a90e4e805a8f274a669bed403f8825d5786517d 100644 (file)
@@ -53,6 +53,7 @@
 #include "gtkplacesviewprivate.h"
 #include "gtkprivate.h"
 #include "gtkrecentmanager.h"
+#include "gtkscrolledwindow.h"
 #include "gtksearchentryprivate.h"
 #include "gtksettings.h"
 #include "gtksingleselection.h"
@@ -180,6 +181,11 @@ typedef enum {
   TYPE_FORMAT_CATEGORY
 } TypeFormat;
 
+typedef enum {
+  VIEW_TYPE_LIST,
+  VIEW_TYPE_GRID,
+} ViewType;
+
 typedef struct _GtkFileChooserWidgetClass   GtkFileChooserWidgetClass;
 
 struct _GtkFileChooserWidget
@@ -211,6 +217,8 @@ struct _GtkFileChooserWidget
   GtkWidget *browse_files_stack;
   GtkWidget *browse_files_swin;
   GtkWidget *browse_files_column_view;
+  GtkWidget *browse_files_grid_view;
+  GtkWidget *browse_toggle_view_button;
   GtkWidget *remote_warning_bar;
 
   GtkWidget *browse_files_popover;
@@ -312,6 +320,8 @@ struct _GtkFileChooserWidget
 
   TypeFormat type_format;
 
+  ViewType view_type;
+
   /* Flags */
 
   guint select_multiple : 1;
@@ -1704,6 +1714,70 @@ popup_file_list_menu (GSimpleAction *action,
   file_list_show_popover (impl, x, y);
 }
 
+static void
+set_view_type (GtkFileChooserWidget *impl,
+               ViewType              view_type)
+{
+  const char *tooltip_text;
+  const char *icon_name;
+  GtkWidget *child;
+
+  if (impl->view_type == view_type)
+    return;
+
+  impl->view_type = view_type;
+
+  g_object_ref (impl->browse_files_grid_view);
+  g_object_ref (impl->browse_files_column_view);
+
+  switch (impl->view_type)
+    {
+    case VIEW_TYPE_LIST:
+      child = impl->browse_files_column_view;
+      icon_name = "view-grid-symbolic";
+      tooltip_text = _("Switch to grid view");
+      break;
+
+    case VIEW_TYPE_GRID:
+      child = impl->browse_files_grid_view;
+      icon_name = "view-list-symbolic";
+      tooltip_text = _("Switch to list view");
+      break;
+
+    default:
+      g_assert_not_reached ();
+    }
+
+  gtk_widget_set_tooltip_text (impl->browse_toggle_view_button, tooltip_text);
+  gtk_button_set_icon_name (GTK_BUTTON (impl->browse_toggle_view_button), icon_name);
+  gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (impl->browse_files_swin), child);
+
+  g_object_unref (impl->browse_files_grid_view);
+  g_object_unref (impl->browse_files_column_view);
+}
+
+static void
+toggle_view_cb (GSimpleAction *action,
+                GVariant      *parameter,
+                gpointer       user_data)
+{
+  GtkFileChooserWidget *impl = user_data;
+
+  switch (impl->view_type)
+    {
+    case VIEW_TYPE_LIST:
+      set_view_type (impl, VIEW_TYPE_GRID);
+      break;
+
+    case VIEW_TYPE_GRID:
+      set_view_type (impl, VIEW_TYPE_LIST);
+      break;
+
+    default:
+      g_assert_not_reached ();
+    }
+}
+
 static GActionEntry entries[] = {
   { "visit", visit_file_cb, NULL, NULL, NULL },
   { "open", open_folder_cb, NULL, NULL, NULL },
@@ -1717,7 +1791,8 @@ static GActionEntry entries[] = {
   { "toggle-show-size", NULL, NULL, "false", change_show_size_state },
   { "toggle-show-type", NULL, NULL, "false", change_show_type_state },
   { "toggle-show-time", NULL, NULL, "false", change_show_time_state },
-  { "toggle-sort-dirs-first", NULL, NULL, "false", change_sort_directories_first_state }
+  { "toggle-sort-dirs-first", NULL, NULL, "false", change_sort_directories_first_state },
+  { "toggle-view", toggle_view_cb, NULL, NULL, NULL },
 };
 
 static void
@@ -6691,12 +6766,14 @@ gtk_file_chooser_widget_class_init (GtkFileChooserWidgetClass *class)
   gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, places_sidebar);
   gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, places_view);
   gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, browse_files_column_view);
+  gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, browse_files_grid_view);
   gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, browse_files_swin);
   gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, browse_header_revealer);
   gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, browse_header_stack);
   gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, browse_new_folder_button);
   gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, browse_path_bar_size_group);
   gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, browse_path_bar);
+  gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, browse_toggle_view_button);
   gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, column_view_name_column);
   gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, column_view_location_column);
   gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, column_view_size_column);
@@ -6830,10 +6907,23 @@ post_process_ui (GtkFileChooserWidget *impl)
   gtk_shortcut_controller_add_shortcut (GTK_SHORTCUT_CONTROLLER (controller), shortcut);
   gtk_widget_add_controller (GTK_WIDGET (impl->browse_files_column_view), controller);
 
+  controller = gtk_shortcut_controller_new ();
+  trigger = gtk_alternative_trigger_new (gtk_keyval_trigger_new (GDK_KEY_F10, GDK_SHIFT_MASK),
+                                         gtk_keyval_trigger_new (GDK_KEY_Menu, 0));
+  action = gtk_callback_action_new (list_popup_menu_cb, impl, NULL);
+  shortcut = gtk_shortcut_new (trigger, action);
+  gtk_shortcut_controller_add_shortcut (GTK_SHORTCUT_CONTROLLER (controller), shortcut);
+  gtk_widget_add_controller (GTK_WIDGET (impl->browse_files_grid_view), controller);
+
   controller = gtk_event_controller_key_new ();
   gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE);
   g_signal_connect (controller, "key-pressed", G_CALLBACK (files_list_restrict_key_presses), impl);
   gtk_widget_add_controller (impl->browse_files_column_view, controller);
+
+  controller = gtk_event_controller_key_new ();
+  gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE);
+  g_signal_connect (controller, "key-pressed", G_CALLBACK (files_list_restrict_key_presses), impl);
+  gtk_widget_add_controller (impl->browse_files_grid_view, controller);
 }
 
 void
@@ -7195,6 +7285,7 @@ gtk_file_chooser_widget_init (GtkFileChooserWidget *impl)
   impl->location_mode = LOCATION_MODE_PATH_BAR;
   impl->operation_mode = OPERATION_MODE_BROWSE;
   impl->sort_order = GTK_SORT_ASCENDING;
+  impl->view_type = VIEW_TYPE_LIST;
   impl->create_folders = TRUE;
   impl->auto_selecting_first_row = FALSE;
   impl->renamed_file = NULL;
index 4a55e83d9ccdd260171d30370fcdfc45b3f3e72f..1eb033008958dafbce99c7d850413ae1f9cd7388 100644 (file)
                                             <signal name="path-clicked" handler="path_bar_clicked" after="yes" swapped="no"/>
                                           </object>
                                         </child>
+                                        <child>
+                                          <object class="GtkButton" id="browse_toggle_view_button">
+                                            <property name="icon-name">view-grid-symbolic</property>
+                                            <property name="action-name">item.toggle-view</property>
+                                            <property name="tooltip-text" translatable="yes">Switch to grid view</property>
+                                          </object>
+                                        </child>
                                         <child>
                                           <object class="GtkMenuButton" id="browse_new_folder_button">
                                             <property name="tooltip-text" translatable="yes">Create Folder</property>
       </object>
     </child>
   </object>
+  <object class="GtkGridView" id="browse_files_grid_view">
+    <signal name="activate" handler="column_view_row_activated_cb" swapped="no"/>
+    <binding name="enable-rubberband">
+      <lookup name="enable-rubberband">browse_files_column_view</lookup>
+    </binding>
+    <binding name="model">
+      <lookup name="model">browse_files_column_view</lookup>
+    </binding>
+    <property name="factory">
+      <object class="GtkBuilderListItemFactory">
+        <property name="bytes"><![CDATA[
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <template class="GtkListItem">
+    <property name="child">
+      <object class="GtkFileChooserCell">
+        <binding name="position">
+          <lookup name="position">GtkListItem</lookup>
+        </binding>
+        <binding name="item">
+          <lookup name="item">GtkListItem</lookup>
+        </binding>
+        <binding name="selected">
+          <lookup name="selected">GtkListItem</lookup>
+        </binding>
+        <child>
+          <object class="GtkBox">
+            <property name="orientation">vertical</property>
+            <binding name="tooltip-text">
+              <closure type="gchararray" function="column_view_get_tooltip_text">
+                <lookup name="item">GtkListItem</lookup>
+              </closure>
+            </binding>
+            <child>
+              <object class="GtkFileThumbnail">
+                <property name="icon-size">96</property>
+                <property name="margin-start">6</property>
+                <property name="margin-end">6</property>
+                <binding name="file-info">
+                  <lookup name="item">GtkListItem</lookup>
+                </binding>
+              </object>
+            </child>
+            <child>
+              <object class="GtkInscription">
+                <property name="hexpand">1</property>
+                <property name="yalign">0.0</property>
+                <property name="xalign">0.5</property>
+                <property name="min-chars">12</property>
+                <property name="min-lines">2</property>
+                <binding name="text">
+                  <closure type="gchararray" function="column_view_get_file_display_name">
+                    <lookup name="item">GtkListItem</lookup>
+                  </closure>
+                </binding>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </property>
+  </template>
+</interface>
+    ]]></property>
+      </object>
+    </property>
+  </object>
 </interface>