[recipes] Populate list pages asynchronously, using search
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [recipes] Populate list pages asynchronously, using search
- Date: Sat, 31 Dec 2016 16:42:19 +0000 (UTC)
commit 0837f35924b976b2bdfabe3a43d18f530c48dec6
Author: Matthias Clasen <mclasen redhat com>
Date: Sat Dec 31 11:39:19 2016 -0500
Populate list pages asynchronously, using search
Use a search to populate the list page with various content.
https://bugzilla.gnome.org/show_bug.cgi?id=775850
There are various small UI refinements here as well:
- The diet page header now has a limited width
- We avoid showing the header until we have enough
search results to keep it from jumping around
- We show the 'no results' placeholder below the header,
since the header information is still useful in that case
src/gr-list-page.c | 261 +++++++++++++++++++++++++++------------------------
src/gr-list-page.ui | 181 ++++++++++++++++++-----------------
2 files changed, 232 insertions(+), 210 deletions(-)
---
diff --git a/src/gr-list-page.c b/src/gr-list-page.c
index 678e1a0..2a7db6a 100644
--- a/src/gr-list-page.c
+++ b/src/gr-list-page.c
@@ -42,6 +42,7 @@ struct _GrListPage
gboolean favorites;
char *season;
+ GtkWidget *top_box;
GtkWidget *list_stack;
GtkWidget *flow_box;
GtkWidget *empty_title;
@@ -53,6 +54,9 @@ struct _GrListPage
GtkWidget *chef_description;
GtkWidget *heading;
GtkWidget *diet_description;
+
+ int count;
+ GrRecipeSearch *search;
};
G_DEFINE_TYPE (GrListPage, gr_list_page, GTK_TYPE_BOX)
@@ -75,15 +79,107 @@ list_page_finalize (GObject *object)
clear_data (self);
+ g_clear_object (&self->search);
+
G_OBJECT_CLASS (gr_list_page_parent_class)->finalize (object);
}
static void
+hide_heading (GrListPage *self)
+{
+ gtk_widget_hide (self->chef_grid);
+ gtk_widget_hide (self->diet_description);
+ gtk_widget_hide (self->heading);
+}
+
+static void
+show_heading (GrListPage *self)
+{
+ if (self->chef) {
+ gtk_widget_show (self->chef_grid);
+ gtk_widget_show (self->heading);
+ }
+ else if (self->diet) {
+ gtk_widget_show (self->diet_description);
+ gtk_widget_show (self->heading);
+ }
+}
+
+static void
+search_started (GrRecipeSearch *search,
+ GrListPage *page)
+{
+ container_remove_all (GTK_CONTAINER (page->flow_box));
+ hide_heading (page);
+ page->count = 0;
+}
+
+static void
+search_hits_added (GrRecipeSearch *search,
+ GList *hits,
+ GrListPage *page)
+{
+ GList *l;
+ int count = page->count;
+
+ for (l = hits; l; l = l->next) {
+ GrRecipe *recipe = l->data;
+ GtkWidget *tile;
+
+ tile = gr_recipe_tile_new (recipe);
+ gtk_widget_show (tile);
+ gtk_container_add (GTK_CONTAINER (page->flow_box), tile);
+
+ page->count++;
+ }
+
+ if (count == 0 && page->count > 0)
+ show_heading (page);
+}
+
+static void
+search_hits_removed (GrRecipeSearch *search,
+ GList *hits,
+ GrListPage *page)
+{
+ GList *children, *l;
+
+ children = gtk_container_get_children (GTK_CONTAINER (page->flow_box));
+ for (l = children; l; l = l->next) {
+ GtkWidget *item = l->data;
+ GtkWidget *tile;
+ GrRecipe *recipe;
+
+ tile = gtk_bin_get_child (GTK_BIN (item));
+ recipe = gr_recipe_tile_get_recipe (GR_RECIPE_TILE (tile));
+ if (g_list_find (hits, recipe)) {
+ gtk_container_remove (GTK_CONTAINER (page->flow_box), item);
+ page->count--;
+ }
+ }
+}
+
+static void
+search_finished (GrRecipeSearch *search,
+ GrListPage *page)
+{
+ show_heading (page);
+ gtk_stack_set_visible_child_name (GTK_STACK (page->list_stack),
+ page->count > 0 ? "list" : "empty");
+}
+
+static void
gr_list_page_init (GrListPage *page)
{
gtk_widget_set_has_window (GTK_WIDGET (page), FALSE);
gtk_widget_init_template (GTK_WIDGET (page));
connect_store_signals (page);
+
+ page->search = gr_recipe_search_new ();
+ g_signal_connect (page->search, "started", G_CALLBACK (search_started), page);
+ g_signal_connect (page->search, "hits-added", G_CALLBACK (search_hits_added), 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);
}
static void
@@ -96,6 +192,7 @@ gr_list_page_class_init (GrListPageClass *klass)
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Recipes/gr-list-page.ui");
+ gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GrListPage, top_box);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GrListPage, flow_box);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GrListPage, list_stack);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GrListPage, empty_title);
@@ -121,42 +218,47 @@ gr_list_page_new (void)
static const char *
get_category_title (GrDiets diet)
{
- const char *label;
-
switch (diet) {
case GR_DIET_GLUTEN_FREE:
- label = _("Gluten-free recipes");
- break;
+ return _("Gluten-free recipes");
case GR_DIET_NUT_FREE:
- label = _("Nut-free recipes");
- break;
+ return _("Nut-free recipes");
case GR_DIET_VEGAN:
- label = _("Vegan recipes");
- break;
+ return _("Vegan recipes");
case GR_DIET_VEGETARIAN:
- label = _("Vegetarian recipes");
- break;
+ return _("Vegetarian recipes");
case GR_DIET_MILK_FREE:
- label = _("Milk-free recipes");
- break;
+ return _("Milk-free recipes");
default:
- label = _("Other dietary restrictions");
- break;
+ return _("Other dietary restrictions");
+ }
}
- return label;
+static const char *
+get_diet_name (GrDiets diet)
+{
+ switch (diet) {
+ case GR_DIET_GLUTEN_FREE:
+ return "gluten-free";
+ case GR_DIET_NUT_FREE:
+ return "nut-free";
+ case GR_DIET_VEGAN:
+ return "vegan";
+ case GR_DIET_VEGETARIAN:
+ return "vegetarian";
+ case GR_DIET_MILK_FREE:
+ return "milk-free";
+ default:
+ return "";
+ }
}
void
gr_list_page_populate_from_diet (GrListPage *self,
GrDiets diet)
{
- GrRecipeStore *store;
- g_autofree char **keys = NULL;
- guint length;
- int i;
- gboolean filled;
char *tmp;
+ g_autofree char *term = NULL;
clear_data (self);
self->diet = diet;
@@ -173,30 +275,11 @@ gr_list_page_populate_from_diet (GrListPage *self,
gtk_label_set_label (GTK_LABEL (self->empty_title), tmp);
g_free (tmp);
gtk_label_set_label (GTK_LABEL (self->empty_subtitle), _("You could add one using the “New Recipe”
button."));
- gtk_stack_set_visible_child_name (GTK_STACK (self->list_stack), "empty");
- filled = FALSE;
-
- store = gr_app_get_recipe_store (GR_APP (g_application_get_default ()));
-
- keys = gr_recipe_store_get_recipe_keys (store, &length);
- for (i = 0; i < length; i++) {
- g_autoptr(GrRecipe) recipe = NULL;
- GtkWidget *tile;
- GrDiets diets;
-
- recipe = gr_recipe_store_get_recipe (store, keys[i]);
- diets = gr_recipe_get_diets (recipe);
- if ((diets & diet) == 0)
- continue;
-
- tile = gr_recipe_tile_new (recipe);
- gtk_widget_show (tile);
- gtk_container_add (GTK_CONTAINER (self->flow_box), tile);
- filled = TRUE;
- }
- if (filled)
- gtk_stack_set_visible_child_name (GTK_STACK (self->list_stack), "list");
+ gr_recipe_search_stop (self->search);
+ gtk_stack_set_visible_child_name (GTK_STACK (self->list_stack), "list");
+ term = g_strconcat ("di:", get_diet_name (diet), NULL);
+ gr_recipe_search_set_query (self->search, term);
}
void
@@ -206,14 +289,12 @@ gr_list_page_populate_from_chef (GrListPage *self,
GrRecipeStore *store;
const char *id;
g_autofree char **keys = NULL;
- guint length;
- int i;
- gboolean filled;
char *tmp;
const char *image_path;
GtkStyleContext *context;
g_autofree char *css = NULL;
g_autoptr(GtkCssProvider) provider = NULL;
+ g_autofree char *term = NULL;
g_object_ref (chef);
clear_data (self);
@@ -266,44 +347,21 @@ gr_list_page_populate_from_chef (GrListPage *self,
gtk_label_set_label (GTK_LABEL (self->empty_subtitle), _("You could add one using the “New
Recipe” button."));
else
gtk_label_set_label (GTK_LABEL (self->empty_subtitle), _("Sorry about this."));
- gtk_stack_set_visible_child_name (GTK_STACK (self->list_stack), "empty");
- filled = FALSE;
id = gr_chef_get_id (chef);
- keys = gr_recipe_store_get_recipe_keys (store, &length);
- for (i = 0; i < length; i++) {
- g_autoptr(GrRecipe) recipe = NULL;
- const char *author;
- GtkWidget *tile;
-
- recipe = gr_recipe_store_get_recipe (store, keys[i]);
- author = gr_recipe_get_author (recipe);
-
- if (g_strcmp0 (id, author) != 0)
- continue;
-
- tile = gr_recipe_tile_new (recipe);
- gtk_widget_show (tile);
- gtk_container_add (GTK_CONTAINER (self->flow_box), tile);
-
- filled = TRUE;
- }
-
- if (filled)
- gtk_stack_set_visible_child_name (GTK_STACK (self->list_stack), "list");
+ gr_recipe_search_stop (self->search);
+ gtk_stack_set_visible_child_name (GTK_STACK (self->list_stack), "list");
+ term = g_strconcat ("by:", id, NULL);
+ gr_recipe_search_set_query (self->search, term);
}
void
gr_list_page_populate_from_season (GrListPage *self,
const char *season)
{
- GrRecipeStore *store;
- g_autofree char **keys = NULL;
- guint length;
- int i;
- gboolean filled;
char *tmp;
+ g_autofree char *term = NULL;
tmp = g_strdup (season);
clear_data (self);
@@ -318,39 +376,16 @@ gr_list_page_populate_from_season (GrListPage *self,
gtk_label_set_label (GTK_LABEL (self->empty_title), tmp);
g_free (tmp);
gtk_label_set_label (GTK_LABEL (self->empty_subtitle), _("You could add one using the “New Recipe”
button."));
- gtk_stack_set_visible_child_name (GTK_STACK (self->list_stack), "empty");
- filled = FALSE;
-
- store = gr_app_get_recipe_store (GR_APP (g_application_get_default ()));
-
- keys = gr_recipe_store_get_recipe_keys (store, &length);
- for (i = 0; i < length; i++) {
- g_autoptr(GrRecipe) recipe = NULL;
- GtkWidget *tile;
-
- recipe = gr_recipe_store_get_recipe (store, keys[i]);
- if (g_strcmp0 (self->season, gr_recipe_get_season (recipe)) != 0)
- continue;
- tile = gr_recipe_tile_new (recipe);
- gtk_widget_show (tile);
- gtk_container_add (GTK_CONTAINER (self->flow_box), tile);
- filled = TRUE;
- }
-
- if (filled)
- gtk_stack_set_visible_child_name (GTK_STACK (self->list_stack), "list");
+ gr_recipe_search_stop (self->search);
+ gtk_stack_set_visible_child_name (GTK_STACK (self->list_stack), "list");
+ term = g_strconcat ("se:", self->season, NULL);
+ gr_recipe_search_set_query (self->search, term);
}
void
gr_list_page_populate_from_favorites (GrListPage *self)
{
- GrRecipeStore *store;
- g_autofree char **keys = NULL;
- guint length;
- int i;
- gboolean filled;
-
clear_data (self);
self->favorites = TRUE;
@@ -361,28 +396,10 @@ gr_list_page_populate_from_favorites (GrListPage *self)
container_remove_all (GTK_CONTAINER (self->flow_box));
gtk_label_set_label (GTK_LABEL (self->empty_title), _("No favorite recipes found"));
gtk_label_set_label (GTK_LABEL (self->empty_subtitle), _("Use the “Cook later” button to mark
recipes as favorites."));
- gtk_stack_set_visible_child_name (GTK_STACK (self->list_stack), "empty");
- filled = FALSE;
-
- store = gr_app_get_recipe_store (GR_APP (g_application_get_default ()));
-
- keys = gr_recipe_store_get_recipe_keys (store, &length);
- for (i = 0; i < length; i++) {
- g_autoptr(GrRecipe) recipe = NULL;
- GtkWidget *tile;
- recipe = gr_recipe_store_get_recipe (store, keys[i]);
- if (!gr_recipe_store_is_favorite (store, recipe))
- continue;
-
- tile = gr_recipe_tile_new (recipe);
- gtk_widget_show (tile);
- gtk_container_add (GTK_CONTAINER (self->flow_box), tile);
- filled = TRUE;
- }
-
- if (filled)
- gtk_stack_set_visible_child_name (GTK_STACK (self->list_stack), "list");
+ gr_recipe_search_stop (self->search);
+ gtk_stack_set_visible_child_name (GTK_STACK (self->list_stack), "list");
+ gr_recipe_search_set_query (self->search, "is:favorite");
}
static void
diff --git a/src/gr-list-page.ui b/src/gr-list-page.ui
index 4158562..1a33c06 100644
--- a/src/gr-list-page.ui
+++ b/src/gr-list-page.ui
@@ -5,130 +5,132 @@
<property name="visible">True</property>
<property name="orientation">vertical</property>
<child>
- <object class="GtkStack" id="list_stack">
+ <object class="GtkScrolledWindow">
<property name="visible">1</property>
+ <property name="expand">1</property>
+ <property name="hscrollbar-policy">never</property>
<child>
- <object class="GtkBox">
+ <object class="GtkBox" id="top_box">
<property name="visible">1</property>
- <property name="halign">center</property>
- <property name="valign">center</property>
<property name="orientation">vertical</property>
- <property name="spacing">10</property>
+ <property name="halign">center</property>
+ <property name="valign">fill</property>
+ <property name="margin-start">80</property>
+ <property name="margin-end">80</property>
<child>
- <object class="GtkImage">
+ <object class="GtkGrid" id="chef_grid">
<property name="visible">1</property>
- <property name="icon-name">org.gnome.Recipes-symbolic</property>
- <property name="pixel-size">128</property>
- <style>
- <class name="dim-label"/>
- </style>
+ <property name="margin-top">20</property>
+ <property name="column-spacing">20</property>
+ <property name="row-spacing">20</property>
+ <child>
+ <object class="GtkImage" id="chef_image">
+ <property name="visible">1</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <style>
+ <class name="image-button"/>
+ <class name="circular"/>
+ <class name="chef"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">0</property>
+ <property name="height">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="chef_fullname">
+ <property name="visible">1</property>
+ <property name="xalign">0</property>
+ <attributes>
+ <attribute name="scale" value="1.44"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="chef_description">
+ <property name="visible">1</property>
+ <property name="xalign">0</property>
+ <property name="wrap">1</property>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">1</property>
+ </packing>
+ </child>
</object>
</child>
<child>
- <object class="GtkLabel" id="empty_title">
+ <object class="GtkLabel" id="diet_description">
<property name="visible">1</property>
- <property name="label" translatable="yes">No recipes found</property>
- <style>
- <class name="dim-label"/>
- </style>
- <attributes>
- <attribute name="scale" value="1.2"/>
- <attribute name="weight" value="bold"/>
- </attributes>
+ <property name="xalign">0</property>
+ <property name="wrap">1</property>
+ <property name="margin-top">20</property>
+ <property name="width-chars">60</property>
+ <property name="max-width-chars">60</property>
</object>
</child>
<child>
- <object class="GtkLabel" id="empty_subtitle">
+ <object class="GtkLabel" id="heading">
<property name="visible">1</property>
- <property name="label" translatable="yes">There are no recipes here yet.</property>
+ <property name="xalign">0</property>
<style>
- <class name="dim-label"/>
+ <class name="heading"/>
</style>
</object>
</child>
- </object>
- <packing>
- <property name="name">empty</property>
- </packing>
- </child>
- <child>
- <object class="GtkScrolledWindow">
- <property name="visible">1</property>
- <property name="expand">1</property>
- <property name="hscrollbar-policy">never</property>
<child>
- <object class="GtkBox">
+ <object class="GtkStack" id="list_stack">
<property name="visible">1</property>
- <property name="orientation">vertical</property>
- <property name="halign">center</property>
- <property name="valign">start</property>
- <property name="margin-start">80</property>
- <property name="margin-end">80</property>
<child>
- <object class="GtkGrid" id="chef_grid">
+ <object class="GtkBox">
<property name="visible">1</property>
- <property name="margin-top">20</property>
- <property name="column-spacing">20</property>
- <property name="row-spacing">20</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">10</property>
<child>
- <object class="GtkImage" id="chef_image">
+ <object class="GtkImage">
<property name="visible">1</property>
- <property name="halign">center</property>
- <property name="valign">center</property>
+ <property name="icon-name">org.gnome.Recipes-symbolic</property>
+ <property name="pixel-size">128</property>
<style>
- <class name="image-button"/>
- <class name="circular"/>
- <class name="chef"/>
+ <class name="dim-label"/>
</style>
</object>
- <packing>
- <property name="left-attach">0</property>
- <property name="top-attach">0</property>
- <property name="height">2</property>
- </packing>
</child>
<child>
- <object class="GtkLabel" id="chef_fullname">
+ <object class="GtkLabel" id="empty_title">
<property name="visible">1</property>
- <property name="xalign">0</property>
+ <property name="label" translatable="yes">No recipes found</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
<attributes>
- <attribute name="scale" value="1.44"/>
+ <attribute name="scale" value="1.2"/>
+ <attribute name="weight" value="bold"/>
</attributes>
</object>
- <packing>
- <property name="left-attach">1</property>
- <property name="top-attach">0</property>
- </packing>
</child>
<child>
- <object class="GtkLabel" id="chef_description">
+ <object class="GtkLabel" id="empty_subtitle">
<property name="visible">1</property>
- <property name="xalign">0</property>
- <property name="wrap">1</property>
+ <property name="label" translatable="yes">There are no recipes here yet.</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
</object>
- <packing>
- <property name="left-attach">1</property>
- <property name="top-attach">1</property>
- </packing>
</child>
</object>
- </child>
- <child>
- <object class="GtkLabel" id="diet_description">
- <property name="visible">1</property>
- <property name="xalign">0</property>
- <property name="wrap">1</property>
- <property name="margin-top">20</property>
- </object>
- </child>
- <child>
- <object class="GtkLabel" id="heading">
- <property name="visible">1</property>
- <property name="xalign">0</property>
- <style>
- <class name="heading"/>
- </style>
- </object>
+ <packing>
+ <property name="name">empty</property>
+ </packing>
</child>
<child>
<object class="GtkFlowBox" id="flow_box">
@@ -143,13 +145,16 @@
<property name="min-children-per-line">3</property>
<property name="max-children-per-line">3</property>
</object>
+ <packing>
+ <property name="name">list</property>
+ </packing>
</child>
</object>
+ <packing>
+ <property name="expand">1</property>
+ </packing>
</child>
</object>
- <packing>
- <property name="name">list</property>
- </packing>
</child>
</object>
</child>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]