[gnome-control-center/wip/gbsneto/list-layout] window: use a listbox



commit 21aa68fd1986a526cc6bfd02ac65cfce08e9e6e5
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date:   Tue May 24 23:52:02 2016 -0300

    window: use a listbox

 shell/cc-window.c |  496 +++++++++++++++++------------------------------------
 shell/window.ui   |   19 +--
 2 files changed, 155 insertions(+), 360 deletions(-)
---
diff --git a/shell/cc-window.c b/shell/cc-window.c
index fba6613..98a6b4b 100644
--- a/shell/cc-window.c
+++ b/shell/cc-window.c
@@ -47,6 +47,16 @@
 #define SEARCH_PAGE "_search"
 #define OVERVIEW_PAGE "_overview"
 
+typedef struct
+{
+  GtkWidget  *row;
+  GIcon      *icon;
+  gchar      *id;
+  gchar      *name;
+  gchar      *description;
+  GtkWidget  *description_label;
+} RowData;
+
 struct _CcWindow
 {
   GtkApplicationWindow parent;
@@ -55,7 +65,8 @@ struct _CcWindow
   GtkWidget  *header;
   GtkWidget  *header2;
   GtkWidget  *header_box;
-  GtkWidget  *main_vbox;
+  GtkWidget  *listbox;
+  GtkWidget  *list_scrolled;
   GtkWidget  *search_scrolled;
   GtkWidget  *top_right_box;
   GtkWidget  *search_button;
@@ -78,6 +89,8 @@ struct _CcWindow
   gchar *filter_string;
   gchar **filter_terms;
 
+  GHashTable *id_to_row;
+
   CcPanel *active_panel;
 };
 
@@ -119,6 +132,77 @@ get_icon_name_from_g_icon (GIcon *gicon)
   return NULL;
 }
 
+/*
+ * RowData functions
+ */
+static RowData*
+row_data_new (const gchar *id,
+              const gchar *name,
+              const gchar *description,
+              GIcon       *icon)
+{
+  GtkWidget *label, *image, *grid;
+  RowData *data;
+
+  data = g_new0 (RowData, 1);
+  data->row = gtk_list_box_row_new ();
+  data->id = g_strdup (id);
+  data->name = g_strdup (name);
+  data->description = g_strdup (description);
+  data->icon = g_object_ref (icon);
+
+  /* Setup the row */
+  grid = g_object_new (GTK_TYPE_GRID,
+                       "visible", TRUE,
+                       "hexpand", TRUE,
+                       "border-width", 12,
+                       "column-spacing", 6,
+                       NULL);
+
+  /* Icon */
+  image = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_BUTTON);
+  gtk_grid_attach (GTK_GRID (grid), image, 0, 0, 1, 1);
+
+  /* Name label */
+  label = g_object_new (GTK_TYPE_LABEL,
+                        "label", name,
+                        "visible", TRUE,
+                        "xalign", 0,
+                        "hexpand", TRUE,
+                        NULL);
+  gtk_grid_attach (GTK_GRID (grid), label, 1, 0, 1, 1);
+
+  /* Description label */
+  label = g_object_new (GTK_TYPE_LABEL,
+                        "label", description,
+                        "visible", FALSE,
+                        "xalign", 0,
+                        "hexpand", TRUE,
+                        "wrap", TRUE,
+                        "max-width-chars", 20,
+                        NULL);
+  gtk_style_context_add_class (gtk_widget_get_style_context (label), "dim-label");
+  gtk_grid_attach (GTK_GRID (grid), label, 1, 1, 1, 1);
+
+  data->description_label = label;
+
+  gtk_container_add (GTK_CONTAINER (data->row), grid);
+  gtk_widget_show (data->row);
+
+  g_object_set_data (G_OBJECT (data->row), "data", data);
+
+  return data;
+}
+
+static void
+row_data_free (RowData *data)
+{
+  g_object_unref (data->icon);
+  g_free (data->description);
+  g_free (data->name);
+  g_free (data);
+}
+
 static gboolean
 activate_panel (CcWindow           *self,
                 const gchar        *id,
@@ -248,292 +332,29 @@ cc_window_set_search_item (CcWindow   *center,
 }
 
 static void
-item_activated_cb (CcShellCategoryView *view,
-                   gchar               *name,
-                   gchar               *id,
-                   CcWindow            *shell)
-{
-  cc_window_set_active_panel_from_id (CC_SHELL (shell), id, NULL, NULL);
-}
-
-static gboolean
-category_focus_out (GtkWidget     *view,
-                    GdkEventFocus *event,
-                    CcWindow      *shell)
+row_selected_cb (GtkListBox    *listbox,
+                 GtkListBoxRow *row,
+                 CcWindow      *self)
 {
-  gtk_icon_view_unselect_all (GTK_ICON_VIEW (view));
-
-  return FALSE;
-}
-
-static gboolean
-category_focus_in (GtkWidget     *view,
-                   GdkEventFocus *event,
-                   CcWindow      *shell)
-{
-  GtkTreePath *path;
-
-  if (!gtk_icon_view_get_cursor (GTK_ICON_VIEW (view), &path, NULL))
+  /*
+   * When the widget is in the destruction process, it emits
+   * a ::row-selected signal with NULL row. Trying to do anything
+   * in this case will result in a segfault, so we have to
+   * check it here.
+   */
+  if (gtk_widget_in_destruction (GTK_WIDGET (self)))
+        return;
+
+  if (row)
     {
-      path = gtk_tree_path_new_from_indices (0, -1);
-      gtk_icon_view_set_cursor (GTK_ICON_VIEW (view), path, NULL, FALSE);
-    }
-
-  gtk_icon_view_select_path (GTK_ICON_VIEW (view), path);
-  gtk_tree_path_free (path);
+      RowData *data = g_object_get_data (G_OBJECT (row), "data");
 
-  return FALSE;
-}
-
-static GList *
-get_item_views (CcWindow *shell)
-{
-  GList *list, *l;
-  GList *res;
-
-  list = gtk_container_get_children (GTK_CONTAINER (shell->main_vbox));
-  res = NULL;
-  for (l = list; l; l = l->next)
-    {
-      if (!CC_IS_SHELL_CATEGORY_VIEW (l->data))
-        continue;
-      res = g_list_append (res, cc_shell_category_view_get_item_view (CC_SHELL_CATEGORY_VIEW (l->data)));
+      cc_window_set_active_panel_from_id (CC_SHELL (self), data->id, NULL, NULL);
     }
-
-  g_list_free (list);
-
-  return res;
-}
-
-static gboolean
-is_prev_direction (GtkWidget *widget,
-                   GtkDirectionType direction)
-{
-  if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR &&
-      direction == GTK_DIR_LEFT)
-    return TRUE;
-  if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL &&
-      direction == GTK_DIR_RIGHT)
-    return TRUE;
-  return FALSE;
-}
-
-static gboolean
-is_next_direction (GtkWidget *widget,
-                   GtkDirectionType direction)
-{
-  if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR &&
-      direction == GTK_DIR_RIGHT)
-    return TRUE;
-  if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL &&
-      direction == GTK_DIR_LEFT)
-    return TRUE;
-  return FALSE;
-}
-
-static GtkTreePath *
-get_first_path (GtkIconView *view)
-{
-  GtkTreeModel *model;
-  GtkTreeIter iter;
-
-  model = gtk_icon_view_get_model (view);
-  if (!gtk_tree_model_get_iter_first (model, &iter))
-    return NULL;
-  return gtk_tree_model_get_path (model, &iter);
-}
-
-static GtkTreePath *
-get_last_path (GtkIconView *view)
-{
-  GtkTreeModel *model;
-  GtkTreeIter iter;
-  GtkTreePath *path;
-  gboolean ret;
-
-  model = gtk_icon_view_get_model (view);
-  if (!gtk_tree_model_get_iter_first (model, &iter))
-    return NULL;
-
-  ret = TRUE;
-  path = NULL;
-
-  while (ret)
-    {
-      g_clear_pointer (&path, gtk_tree_path_free);
-      path = gtk_tree_model_get_path (model, &iter);
-      ret = gtk_tree_model_iter_next (model, &iter);
-    }
-  return path;
-}
-
-static gboolean
-categories_keynav_failed (GtkIconView      *current_view,
-                          GtkDirectionType  direction,
-                          CcWindow         *shell)
-{
-  GList *views, *v;
-  GtkIconView *new_view;
-  GtkTreePath *path;
-  GtkTreeModel *model;
-  GtkTreeIter iter;
-  gint col, c, dist, d;
-  GtkTreePath *sel;
-  gboolean res;
-
-  res = FALSE;
-
-  views = get_item_views (shell);
-
-  for (v = views; v; v = v->next)
-    {
-      if (v->data == current_view)
-        break;
-    }
-
-  new_view = NULL;
-
-  if (direction == GTK_DIR_DOWN && v != NULL && v->next != NULL)
-    {
-      new_view = v->next->data;
-
-      if (gtk_icon_view_get_cursor (current_view, &path, NULL))
-        {
-          col = gtk_icon_view_get_item_column (current_view, path);
-          gtk_tree_path_free (path);
-
-          sel = NULL;
-          dist = 1000;
-          model = gtk_icon_view_get_model (new_view);
-          g_assert (gtk_tree_model_get_iter_first (model, &iter));
-          do {
-            path = gtk_tree_model_get_path (model, &iter);
-            c = gtk_icon_view_get_item_column (new_view, path);
-            d = ABS (c - col);
-            if (d < dist)
-              {
-                if (sel)
-                  gtk_tree_path_free (sel);
-                sel = path;
-                dist = d;
-              }
-            else
-              gtk_tree_path_free (path);
-          } while (gtk_tree_model_iter_next (model, &iter));
-
-          gtk_icon_view_set_cursor (new_view, sel, NULL, FALSE);
-          gtk_tree_path_free (sel);
-        }
-
-      gtk_widget_grab_focus (GTK_WIDGET (new_view));
-
-      res = TRUE;
-    }
-
-  if (direction == GTK_DIR_UP && v != NULL && v->prev != NULL)
-    {
-      new_view = v->prev->data;
-
-      if (gtk_icon_view_get_cursor (current_view, &path, NULL))
-        {
-          col = gtk_icon_view_get_item_column (current_view, path);
-          gtk_tree_path_free (path);
-
-          sel = NULL;
-          dist = 1000;
-          model = gtk_icon_view_get_model (new_view);
-          g_assert (gtk_tree_model_get_iter_first (model, &iter));
-          do {
-            path = gtk_tree_model_get_path (model, &iter);
-            c = gtk_icon_view_get_item_column (new_view, path);
-            d = ABS (c - col);
-            if (d <= dist)
-              {
-                if (sel)
-                  gtk_tree_path_free (sel);
-                sel = path;
-                dist = d;
-              }
-            else
-              gtk_tree_path_free (path);
-          } while (gtk_tree_model_iter_next (model, &iter));
-
-          gtk_icon_view_set_cursor (new_view, sel, NULL, FALSE);
-          gtk_tree_path_free (sel);
-        }
-
-      gtk_widget_grab_focus (GTK_WIDGET (new_view));
-
-      res = TRUE;
-    }
-
-  if (is_prev_direction (GTK_WIDGET (current_view), direction) && v != NULL)
-    {
-      if (gtk_icon_view_get_cursor (current_view, &path, NULL))
-        {
-          if (v->prev)
-            new_view = v->prev->data;
-
-          if (gtk_tree_path_prev (path))
-            {
-              new_view = current_view;
-            }
-          else if (new_view != NULL)
-            {
-              path = get_last_path (new_view);
-            }
-          else
-            {
-              goto out;
-            }
-
-          gtk_icon_view_set_cursor (new_view, path, NULL, FALSE);
-          gtk_icon_view_select_path (new_view, path);
-          gtk_tree_path_free (path);
-          gtk_widget_grab_focus (GTK_WIDGET (new_view));
-
-          res = TRUE;
-        }
-    }
-
-  if (is_next_direction (GTK_WIDGET (current_view), direction) && v != NULL)
+  else
     {
-      if (gtk_icon_view_get_cursor (current_view, &path, NULL))
-        {
-          GtkTreeIter iter;
-
-          if (v->next)
-            new_view = v->next->data;
-
-          gtk_tree_path_next (path);
-          model = gtk_icon_view_get_model (current_view);
-
-          if (gtk_tree_model_get_iter (model, &iter, path))
-            {
-              new_view = current_view;
-            }
-          else if (new_view != NULL)
-            {
-              path = get_first_path (new_view);
-            }
-          else
-            {
-              goto out;
-            }
-
-          gtk_icon_view_set_cursor (new_view, path, NULL, FALSE);
-          gtk_icon_view_select_path (new_view, path);
-          gtk_tree_path_free (path);
-          gtk_widget_grab_focus (GTK_WIDGET (new_view));
-
-          res = TRUE;
-        }
+      shell_show_overview_page (self);
     }
-
-out:
-  g_list_free (views);
-
-  return res;
 }
 
 static gboolean
@@ -559,18 +380,6 @@ model_filter_func (GtkTreeModel *model,
   return matches;
 }
 
-static gboolean
-category_filter_func (GtkTreeModel    *model,
-                      GtkTreeIter     *iter,
-                      CcPanelCategory  filter)
-{
-  guint category;
-
-  gtk_tree_model_get (model, iter, COL_CATEGORY, &category, -1);
-
-  return (category == filter);
-}
-
 static void
 search_entry_changed_cb (GtkEntry *entry,
                          CcWindow *self)
@@ -797,61 +606,42 @@ setup_search (CcWindow *self)
 }
 
 static void
-add_category_view (CcWindow        *shell,
-                   CcPanelCategory  category,
-                   const char      *name)
+setup_model (CcWindow *shell)
 {
-  GtkTreeModel *filter;
-  GtkWidget *categoryview;
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+
+  shell->store = (GtkListStore *) cc_shell_model_new ();
+  model = GTK_TREE_MODEL (shell->store);
+
+  cc_panel_loader_fill_model (CC_SHELL_MODEL (shell->store));
 
-  if (category > 0)
+  /* Create a row for each panel */
+  gtk_tree_model_get_iter_first (model, &iter);
+
+  while (gtk_tree_model_iter_next (model, &iter))
     {
-      GtkWidget *separator;
-      separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
-      gtk_widget_set_margin_top (separator, 11);
-      gtk_widget_set_margin_bottom (separator, 10);
-      gtk_box_pack_start (GTK_BOX (shell->main_vbox), separator, FALSE, FALSE, 0);
-      gtk_widget_show (separator);
-    }
+      RowData *data;
+      GIcon *icon;
+      gchar *name, *description, *id;
 
-  /* create new category view for this category */
-  filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (shell->store),
-                                      NULL);
-  gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter),
-                                          (GtkTreeModelFilterVisibleFunc) category_filter_func,
-                                          GINT_TO_POINTER (category), NULL);
-
-  categoryview = cc_shell_category_view_new (name, filter);
-  gtk_box_pack_start (GTK_BOX (shell->main_vbox), categoryview, FALSE, TRUE, 0);
-
-  g_signal_connect (cc_shell_category_view_get_item_view (CC_SHELL_CATEGORY_VIEW (categoryview)),
-                    "desktop-item-activated",
-                    G_CALLBACK (item_activated_cb), shell);
-
-  gtk_widget_show (categoryview);
-
-  g_signal_connect (cc_shell_category_view_get_item_view (CC_SHELL_CATEGORY_VIEW (categoryview)),
-                    "focus-in-event",
-                    G_CALLBACK (category_focus_in), shell);
-  g_signal_connect (cc_shell_category_view_get_item_view (CC_SHELL_CATEGORY_VIEW (categoryview)),
-                    "focus-out-event",
-                    G_CALLBACK (category_focus_out), shell);
-  g_signal_connect (cc_shell_category_view_get_item_view (CC_SHELL_CATEGORY_VIEW (categoryview)),
-                    "keynav-failed",
-                    G_CALLBACK (categories_keynav_failed), shell);
-}
+      gtk_tree_model_get (model, &iter,
+                          COL_DESCRIPTION, &description,
+                          COL_GICON, &icon,
+                          COL_ID, &id,
+                          COL_NAME, &name,
+                          -1);
 
-static void
-setup_model (CcWindow *shell)
-{
- shell->store = (GtkListStore *) cc_shell_model_new ();
+      data = row_data_new (id, name, description, icon);
 
-  /* Add categories */
-  add_category_view (shell, CC_CATEGORY_PERSONAL, C_("category", "Personal"));
-  add_category_view (shell, CC_CATEGORY_HARDWARE, C_("category", "Hardware"));
-  add_category_view (shell, CC_CATEGORY_SYSTEM, C_("category", "System"));
+      g_hash_table_insert (shell->id_to_row, id, data);
 
-  cc_panel_loader_fill_model (CC_SHELL_MODEL (shell->store));
+      gtk_container_add (GTK_CONTAINER (shell->listbox), data->row);
+
+      g_clear_pointer (&description, g_free);
+      g_clear_pointer (&name, g_free);
+      g_clear_object (&icon);
+    }
 }
 
 static void
@@ -1152,6 +942,9 @@ cc_window_finalize (GObject *object)
   g_free (self->filter_string);
   g_strfreev (self->filter_terms);
 
+  g_hash_table_remove_all (self->id_to_row);
+  g_hash_table_destroy (self->id_to_row);
+
   G_OBJECT_CLASS (cc_window_parent_class)->finalize (object);
 }
 
@@ -1182,8 +975,8 @@ cc_window_class_init (CcWindowClass *klass)
   gtk_widget_class_bind_template_child (widget_class, CcWindow, header2);
   gtk_widget_class_bind_template_child (widget_class, CcWindow, header_box);
   gtk_widget_class_bind_template_child (widget_class, CcWindow, header_sizegroup);
+  gtk_widget_class_bind_template_child (widget_class, CcWindow, list_scrolled);
   gtk_widget_class_bind_template_child (widget_class, CcWindow, lock_button);
-  gtk_widget_class_bind_template_child (widget_class, CcWindow, main_vbox);
   gtk_widget_class_bind_template_child (widget_class, CcWindow, search_bar);
   gtk_widget_class_bind_template_child (widget_class, CcWindow, search_button);
   gtk_widget_class_bind_template_child (widget_class, CcWindow, search_entry);
@@ -1191,6 +984,7 @@ cc_window_class_init (CcWindowClass *klass)
   gtk_widget_class_bind_template_child (widget_class, CcWindow, top_right_box);
 
   gtk_widget_class_bind_template_callback (widget_class, gdk_window_set_cb);
+  gtk_widget_class_bind_template_callback (widget_class, row_selected_cb);
   gtk_widget_class_bind_template_callback (widget_class, search_entry_changed_cb);
   gtk_widget_class_bind_template_callback (widget_class, search_entry_key_press_event_cb);
   gtk_widget_class_bind_template_callback (widget_class, sidelist_size_allocate_cb);
@@ -1300,6 +1094,19 @@ create_window (CcWindow *self)
   gtk_window_set_titlebar (GTK_WINDOW (self), self->header_box);
   gtk_widget_show_all (self->header_box);
 
+  /*
+   * We have to create the listbox here because declaring it in window.ui
+   * and letting GtkBuilder handle it would hit the bug where the focus is
+   * not tracked.
+   */
+  self->listbox = gtk_list_box_new ();
+  gtk_list_box_set_selection_mode (GTK_LIST_BOX (self->listbox), GTK_SELECTION_SINGLE);
+
+  g_signal_connect (self->listbox, "row-selected", G_CALLBACK (row_selected_cb), self);
+
+  gtk_container_add (GTK_CONTAINER (self->list_scrolled), self->listbox);
+  gtk_widget_show (self->listbox);
+
   setup_model (self);
   create_search_page (self);
 
@@ -1316,6 +1123,11 @@ cc_window_init (CcWindow *self)
 {
   gtk_widget_init_template (GTK_WIDGET (self));
 
+  self->id_to_row = g_hash_table_new_full (g_str_hash,
+                                           g_str_equal,
+                                           g_free,
+                                           (GDestroyNotify) row_data_free);
+
   create_window (self);
 
   self->previous_panels = g_queue_new ();
diff --git a/shell/window.ui b/shell/window.ui
index e6ceb33..b0cd3cd 100644
--- a/shell/window.ui
+++ b/shell/window.ui
@@ -45,28 +45,11 @@
               </packing>
             </child>
             <child>
-              <object class="GtkScrolledWindow">
+              <object class="GtkScrolledWindow" id="list_scrolled">
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="vexpand">True</property>
                 <property name="hscrollbar_policy">never</property>
-                <child>
-                  <object class="GtkViewport">
-                    <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <child>
-                      <object class="GtkBox" id="main_vbox">
-                        <property name="visible">True</property>
-                        <property name="can_focus">False</property>
-                        <property name="border_width">12</property>
-                        <property name="orientation">vertical</property>
-                        <child>
-                          <placeholder/>
-                        </child>
-                      </object>
-                    </child>
-                  </object>
-                </child>
                 <style>
                   <class name="view"/>
                 </style>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]