[recipes] Sort lists by name or recency



commit 5f1ff5fa6eb7f9f592f6247275cef87cb54e4a81
Author: Matthias Clasen <mclasen redhat com>
Date:   Mon Jun 26 23:12:06 2017 -0400

    Sort lists by name or recency
    
    Following recent mockups, we now allow to sort lists by either
    name or recency. We persist this as a setting.

 data/org.gnome.Recipes.gschema.xml |   15 ++++++-
 src/gr-list-page.c                 |   53 +++++++++++++++++++++--
 src/gr-list-page.h                 |    5 ++
 src/gr-search-page.c               |   54 +++++++++++++++++++++--
 src/gr-window.c                    |   65 +++++++++++++++++++++------
 src/gr-window.ui                   |   84 ++++++++++++++++++++++++++++++++++-
 6 files changed, 248 insertions(+), 28 deletions(-)
---
diff --git a/data/org.gnome.Recipes.gschema.xml b/data/org.gnome.Recipes.gschema.xml
index c822a6d..eafda34 100644
--- a/data/org.gnome.Recipes.gschema.xml
+++ b/data/org.gnome.Recipes.gschema.xml
@@ -7,6 +7,11 @@
     <value nick="locale" value="2"/>
   </enum>
 
+  <enum id="org.gnome.recipes.SortKey">
+    <value nick="name" value="0"/>
+    <value nick="recency" value="1"/>
+  </enum>
+
   <schema path="/org/gnome/recipes/" id="org.gnome.Recipes" gettext-domain="gnome-recipes">
 
     <key type="s" name="user">
@@ -68,8 +73,7 @@
         The list of recipes to be exported. Each entry in the list is the ID of a recipe.
       </description>
     </key>
-
-      <key name="temperature-unit" enum="org.gnome.recipes.TemperatureUnit">
+    <key name="temperature-unit" enum="org.gnome.recipes.TemperatureUnit">
       <default>'locale'</default>
       <summary>The setting for which unit temperatures should be displayed in. </summary>
       <description>
@@ -77,6 +81,13 @@
         which means to use the LC_MEASUREMENT category of the current locale to decide.
       </description>
     </key>
+    <key name="sort-key" enum="org.gnome.recipes.SortKey">
+      <default>'name'</default>
+      <summary>By which key to sort lists.</summary>
+      <description>
+        The setting determines which key is used to sort lists of recipes by.
+      </description>
+    </key>
 
   </schema>
 
diff --git a/src/gr-list-page.c b/src/gr-list-page.c
index 69759cc..f59749c 100644
--- a/src/gr-list-page.c
+++ b/src/gr-list-page.c
@@ -32,6 +32,7 @@
 #include "gr-category-tile.h"
 #include "gr-image.h"
 #include "gr-app.h"
+#include "gr-settings.h"
 
 struct _GrListPage
 {
@@ -191,9 +192,9 @@ search_finished (GrRecipeSearch *search,
 }
 
 static int
-sort_func (GtkFlowBoxChild *child1,
-           GtkFlowBoxChild *child2,
-           gpointer         data)
+name_sort_func (GtkFlowBoxChild *child1,
+                GtkFlowBoxChild *child2,
+                gpointer         data)
 {
         GtkWidget *tile1 = gtk_bin_get_child (GTK_BIN (child1));
         GtkWidget *tile2 = gtk_bin_get_child (GTK_BIN (child2));
@@ -205,6 +206,49 @@ sort_func (GtkFlowBoxChild *child1,
         return strcmp (name1, name2);
 }
 
+static int
+date_sort_func (GtkFlowBoxChild *child1,
+                GtkFlowBoxChild *child2,
+                gpointer         data)
+{
+        GtkWidget *tile1 = gtk_bin_get_child (GTK_BIN (child1));
+        GtkWidget *tile2 = gtk_bin_get_child (GTK_BIN (child2));
+        GrRecipe *recipe1 = gr_recipe_tile_get_recipe (GR_RECIPE_TILE (tile1));
+        GrRecipe *recipe2 = gr_recipe_tile_get_recipe (GR_RECIPE_TILE (tile2));
+        GDateTime *mtime1 = gr_recipe_get_mtime (recipe1);
+        GDateTime *mtime2 = gr_recipe_get_mtime (recipe2);
+
+        return g_date_time_compare (mtime2, mtime1);
+}
+
+static void
+gr_list_page_set_sort (GrListPage *page,
+                       GrSortKey   sort)
+{
+        switch (sort) {
+        case SORT_BY_NAME:
+                gtk_flow_box_set_sort_func (GTK_FLOW_BOX (page->flow_box), name_sort_func, page, NULL);
+                break;
+        case SORT_BY_RECENCY:
+                gtk_flow_box_set_sort_func (GTK_FLOW_BOX (page->flow_box), date_sort_func, page, NULL);
+                break;
+        default:
+                g_assert_not_reached ();
+        }
+}
+
+static void
+sort_key_changed (GrListPage *page)
+{
+        GrSortKey sort;
+
+        if (!gtk_widget_get_visible (GTK_WIDGET (page)))
+                return;
+
+        sort = g_settings_get_enum (gr_settings_get (), "sort-key");
+        gr_list_page_set_sort (page, sort);
+}
+
 static void
 gr_list_page_init (GrListPage *page)
 {
@@ -218,7 +262,8 @@ gr_list_page_init (GrListPage *page)
         g_signal_connect (page->search, "hits-removed", G_CALLBACK (search_hits_removed), page);
         g_signal_connect (page->search, "finished", G_CALLBACK (search_finished), page);
 
-        gtk_flow_box_set_sort_func (GTK_FLOW_BOX (page->flow_box), sort_func, page, NULL);
+        g_signal_connect_swapped (gr_settings_get (), "changed::sort-key", G_CALLBACK (sort_key_changed), 
page);
+        g_signal_connect (page, "notify::visible", G_CALLBACK (sort_key_changed), NULL);
 }
 
 static void
diff --git a/src/gr-list-page.h b/src/gr-list-page.h
index 4c33c02..b696eb0 100644
--- a/src/gr-list-page.h
+++ b/src/gr-list-page.h
@@ -46,4 +46,9 @@ void            gr_list_page_populate_from_new       (GrListPage *self);
 void            gr_list_page_clear                   (GrListPage *self);
 void            gr_list_page_repopulate              (GrListPage *self);
 
+typedef enum {
+        SORT_BY_NAME,
+        SORT_BY_RECENCY
+} GrSortKey;
+
 G_END_DECLS
diff --git a/src/gr-search-page.c b/src/gr-search-page.c
index 6e1dcdc..4faeb45 100644
--- a/src/gr-search-page.c
+++ b/src/gr-search-page.c
@@ -28,6 +28,8 @@
 #include "gr-recipe.h"
 #include "gr-recipe-tile.h"
 #include "gr-utils.h"
+#include "gr-list-page.h"
+#include "gr-settings.h"
 
 
 struct _GrSearchPage
@@ -113,9 +115,9 @@ search_finished (GrRecipeSearch *search,
 }
 
 static int
-sort_func (GtkFlowBoxChild *child1,
-           GtkFlowBoxChild *child2,
-           gpointer         data)
+name_sort_func (GtkFlowBoxChild *child1,
+                GtkFlowBoxChild *child2,
+                gpointer         data)
 {
         GtkWidget *tile1 = gtk_bin_get_child (GTK_BIN (child1));
         GtkWidget *tile2 = gtk_bin_get_child (GTK_BIN (child2));
@@ -127,6 +129,49 @@ sort_func (GtkFlowBoxChild *child1,
         return strcmp (name1, name2);
 }
 
+static int
+date_sort_func (GtkFlowBoxChild *child1,
+                GtkFlowBoxChild *child2,
+                gpointer         data)
+{
+        GtkWidget *tile1 = gtk_bin_get_child (GTK_BIN (child1));
+        GtkWidget *tile2 = gtk_bin_get_child (GTK_BIN (child2));
+        GrRecipe *recipe1 = gr_recipe_tile_get_recipe (GR_RECIPE_TILE (tile1));
+        GrRecipe *recipe2 = gr_recipe_tile_get_recipe (GR_RECIPE_TILE (tile2));
+        GDateTime *mtime1 = gr_recipe_get_mtime (recipe1);
+        GDateTime *mtime2 = gr_recipe_get_mtime (recipe2);
+
+        return g_date_time_compare (mtime2, mtime1);
+}
+
+static void
+gr_search_page_set_sort (GrSearchPage *page,
+                         GrSortKey   sort)
+{
+        switch (sort) {
+        case SORT_BY_NAME:
+                gtk_flow_box_set_sort_func (GTK_FLOW_BOX (page->flow_box), name_sort_func, page, NULL);
+                break;
+        case SORT_BY_RECENCY:
+                gtk_flow_box_set_sort_func (GTK_FLOW_BOX (page->flow_box), date_sort_func, page, NULL);
+                break;
+        default:
+                g_assert_not_reached ();
+        }
+}
+
+static void
+sort_key_changed (GrSearchPage *page)
+{
+        GrSortKey sort;
+
+        if (!gtk_widget_get_visible (GTK_WIDGET (page)))
+                return;
+
+        sort = g_settings_get_enum (gr_settings_get (), "sort-key");
+        gr_search_page_set_sort (page, sort);
+}
+
 static void
 gr_search_page_init (GrSearchPage *page)
 {
@@ -140,7 +185,8 @@ gr_search_page_init (GrSearchPage *page)
         g_signal_connect (page->search, "hits-removed", G_CALLBACK (search_hits_removed), page);
         g_signal_connect (page->search, "finished", G_CALLBACK (search_finished), page);
 
-        gtk_flow_box_set_sort_func (GTK_FLOW_BOX (page->flow_box), sort_func, page, NULL);
+        g_signal_connect_swapped (gr_settings_get (), "changed::sort-key", G_CALLBACK (sort_key_changed), 
page);
+        g_signal_connect (page, "notify::visible", G_CALLBACK (sort_key_changed), NULL);
 }
 
 static void
diff --git a/src/gr-window.c b/src/gr-window.c
index 6038dc6..6402b07 100644
--- a/src/gr-window.c
+++ b/src/gr-window.c
@@ -46,6 +46,7 @@
 #include "gr-account.h"
 #include "gr-utils.h"
 #include "gr-appdata.h"
+#include "gr-settings.h"
 
 
 struct _GrWindow
@@ -75,6 +76,11 @@ struct _GrWindow
         GtkWidget *image_page;
         GtkWidget *cooking_page;
 
+        GtkWidget *sort_by_label;
+        GtkWidget *sort_by_name_button;
+        GtkWidget *sort_by_recency_button;
+        GtkWidget *sort_popover;
+
         GtkWidget *undo_revealer;
         GtkWidget *undo_label;
         GrRecipe  *undo_recipe;
@@ -298,7 +304,7 @@ search_mode_changed (GrWindow *window)
 static void
 switch_to_search (GrWindow *window)
 {
-        configure_window (window, "", "main", "main", "search", "search");
+        configure_window (window, "", "main", "main", "list", "search");
 }
 
 void
@@ -541,17 +547,6 @@ window_delete_handler (GtkWidget *widget)
         return FALSE;
 }
 
-static void
-hide_or_show_header_end_stack (GObject    *object,
-                               GParamSpec *pspec,
-                               GrWindow   *window)
-{
-        const char *visible;
-
-        visible = gtk_stack_get_visible_child_name (GTK_STACK (window->header_end_stack));
-        gtk_widget_set_visible (window->header_end_stack, strcmp (visible, "list") != 0);
-}
-
 GrWindow *
 gr_window_new (GrApp *app)
 {
@@ -876,6 +871,37 @@ make_save_sensitive (GrEditPage *edit_page,
 }
 
 static void
+update_sort_menu (GrWindow *window)
+{
+        switch (g_settings_get_enum (gr_settings_get (), "sort-key")) {
+        case SORT_BY_NAME:
+                g_object_set (window->sort_by_name_button, "active", TRUE, NULL);
+                g_object_set (window->sort_by_recency_button, "active", FALSE, NULL);
+                g_object_set (window->sort_by_label, "label", _("Sorted by Name"), NULL);
+                break;
+        case SORT_BY_RECENCY:
+                g_object_set (window->sort_by_name_button, "active", FALSE, NULL);
+                g_object_set (window->sort_by_recency_button, "active", TRUE, NULL);
+                g_object_set (window->sort_by_label, "label", _("Sorted by Recency"), NULL);
+                break;
+        default:
+                g_assert_not_reached ();
+        }
+}
+
+static void
+sort_clicked (GtkWidget *button,
+              GrWindow  *window)
+{
+        GSettings *settings = gr_settings_get ();
+
+        gtk_popover_popdown (GTK_POPOVER (window->sort_popover));
+        g_settings_set_enum (settings, "sort-key",
+                             button == window->sort_by_name_button ? SORT_BY_NAME : SORT_BY_RECENCY);
+        update_sort_menu (window);
+}
+
+static void
 gr_window_class_init (GrWindowClass *klass)
 {
         GObjectClass *object_class = G_OBJECT_CLASS (klass);
@@ -913,13 +939,16 @@ gr_window_class_init (GrWindowClass *klass)
         gtk_widget_class_bind_template_child (widget_class, GrWindow, remind_label);
         gtk_widget_class_bind_template_child (widget_class, GrWindow, shopping_added_revealer);
         gtk_widget_class_bind_template_child (widget_class, GrWindow, shopping_done_revealer);
+        gtk_widget_class_bind_template_child (widget_class, GrWindow, sort_by_label);
+        gtk_widget_class_bind_template_child (widget_class, GrWindow, sort_by_name_button);
+        gtk_widget_class_bind_template_child (widget_class, GrWindow, sort_by_recency_button);
+        gtk_widget_class_bind_template_child (widget_class, GrWindow, sort_popover);
 
         gtk_widget_class_bind_template_callback (widget_class, new_recipe);
         gtk_widget_class_bind_template_callback (widget_class, go_back);
         gtk_widget_class_bind_template_callback (widget_class, add_recipe);
         gtk_widget_class_bind_template_callback (widget_class, visible_page_changed);
         gtk_widget_class_bind_template_callback (widget_class, start_cooking);
-        gtk_widget_class_bind_template_callback (widget_class, hide_or_show_header_end_stack);
         gtk_widget_class_bind_template_callback (widget_class, search_changed);
         gtk_widget_class_bind_template_callback (widget_class, stop_search);
         gtk_widget_class_bind_template_callback (widget_class, search_mode_changed);
@@ -939,6 +968,7 @@ gr_window_class_init (GrWindowClass *klass)
         gtk_widget_class_bind_template_callback (widget_class, close_shopping_done);
         gtk_widget_class_bind_template_callback (widget_class, back_to_shopping);
         gtk_widget_class_bind_template_callback (widget_class, make_save_sensitive);
+        gtk_widget_class_bind_template_callback (widget_class, sort_clicked);
 }
 
 static GtkClipboard *
@@ -995,13 +1025,18 @@ gr_window_init (GrWindow *self)
         g_action_map_add_action_entries (G_ACTION_MAP (self),
                                          entries, G_N_ELEMENTS (entries),
                                          self);
+
+        update_sort_menu (self);
 }
 
 void
 gr_window_go_back (GrWindow *window)
 {
-        if (g_queue_is_empty (window->back_entry_stack)) {
-                configure_window (window, _("Recipes"), "main", "main", "search", "recipes");
+        if (gtk_search_bar_get_search_mode (GTK_SEARCH_BAR (window->search_bar))) {
+                gtk_search_bar_set_search_mode (GTK_SEARCH_BAR (window->search_bar), FALSE);
+        }
+        else if (g_queue_is_empty (window->back_entry_stack)) {
+                configure_window (window, _("Recipes"), "main", "main", "main", "recipes");
         }
         else {
                 go_back (window);
diff --git a/src/gr-window.ui b/src/gr-window.ui
index 874a56b..0bac3f7 100644
--- a/src/gr-window.ui
+++ b/src/gr-window.ui
@@ -85,7 +85,6 @@
           <object class="GtkStack" id="header_end_stack">
             <property name="visible">1</property>
             <property name="hhomogeneous">0</property>
-            <signal name="notify::visible-child-name" handler="hide_or_show_header_end_stack"/>
             <child>
               <object class="GtkToggleButton" id="search_button">
                 <property name="visible">1</property>
@@ -106,7 +105,7 @@
                 </child>
               </object>
               <packing>
-                <property name="name">search</property>
+                <property name="name">main</property>
               </packing>
             </child>
             <child>
@@ -152,8 +151,61 @@
               </packing>
             </child>
             <child>
-              <object class="GtkEventBox">
+              <object class="GtkBox">
                 <property name="visible">1</property>
+                <property name="orientation">horizontal</property>
+                <property name="spacing">6</property>
+                <child>
+                  <object class="GtkMenuButton">
+                    <property name="visible">1</property>
+                    <property name="popover">sort_popover</property>
+                    <style>
+                      <class name="text-button"/>
+                    </style>
+                    <child>
+                      <object class="GtkBox">
+                        <property name="visible">1</property>
+                        <property name="orientation">horizontal</property>
+                        <property name="spacing">10</property>
+                        <property name="halign">fill</property>
+                        <child>
+                          <object class="GtkLabel" id="sort_by_label">
+                            <property name="visible">1</property>
+                            <property name="label">Sorted by Name</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkImage">
+                            <property name="visible">1</property>
+                            <property name="icon-name">pan-down-symbolic</property>
+                            <property name="icon-size">1</property>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkToggleButton" id="search_button2">
+                    <property name="visible">1</property>
+                    <property name="active" bind-source="search_button" bind-property="active" 
bind-flags="bidirectional"/>
+                    <style>
+                      <class name="image-button"/>
+                    </style>
+                    <child internal-child="accessible">
+                      <object class="AtkObject">
+                        <property name="accessible-name" translatable="yes">Search</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkImage">
+                        <property name="visible">1</property>
+                        <property name="icon-name">edit-find-symbolic</property>
+                        <property name="icon-size">1</property>
+                      </object>
+                    </child>
+                  </object>
+                </child>
               </object>
               <packing>
                 <property name="name">list</property>
@@ -505,4 +557,30 @@
       </object>
     </child>
   </template>
+  <object class="GtkPopover" id="sort_popover">
+    <child>
+      <object class="GtkBox">
+        <property name="visible">1</property>
+        <property name="orientation">vertical</property>
+        <property name="margin">10</property>
+        <child>
+          <object class="GtkModelButton" id="sort_by_name_button">
+            <property name="visible">1</property>
+            <property name="role">radio</property>
+            <property name="active">1</property>
+            <property name="text" translatable="yes">Name</property>
+            <signal name="clicked" handler="sort_clicked"/>
+          </object>
+        </child>
+        <child>
+          <object class="GtkModelButton" id="sort_by_recency_button">
+            <property name="visible">1</property>
+            <property name="role">radio</property>
+            <property name="text" translatable="yes">Recency</property>
+            <signal name="clicked" handler="sort_clicked"/>
+          </object>
+        </child>
+      </object>
+    </child>
+  </object>
 </interface>


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