[recipes] Add a unit completion list



commit 45fa1928cff73ceee39d08eea846f12e222eefdc
Author: Matthias Clasen <mclasen redhat com>
Date:   Thu Dec 22 21:46:06 2016 -0500

    Add a unit completion list
    
    Stop using a combo box for units; this is not what we want,
    ultimatively. Instead add a completion list similar to what
    we do for ingredients.

 src/gr-edit-page.c  |  215 +++++++++++++++++++++++++++++++++++++++++++++++++--
 src/gr-edit-page.ui |   82 +++++++++++++++++---
 2 files changed, 278 insertions(+), 19 deletions(-)
---
diff --git a/src/gr-edit-page.c b/src/gr-edit-page.c
index a02b085..a5f0b64 100644
--- a/src/gr-edit-page.c
+++ b/src/gr-edit-page.c
@@ -88,6 +88,11 @@ struct _GrEditPage
         GtkWidget *ing_search_button_label;
         GtkWidget *ing_search_revealer;
 
+        GtkWidget *unit_list;
+        GtkWidget *amount_search_button;
+        GtkWidget *amount_search_button_label;
+        GtkWidget *amount_search_revealer;
+
         GtkSizeGroup *group;
 
         guint account_response_signal_id;
@@ -96,6 +101,7 @@ struct _GrEditPage
         GtkWidget *active_row;
 
         char *ing_term;
+        char *unit_term;
 };
 
 G_DEFINE_TYPE (GrEditPage, gr_edit_page, GTK_TYPE_BOX)
@@ -154,6 +160,7 @@ edit_page_finalize (GObject *object)
         g_list_free (self->segments);
 
         g_free (self->ing_term);
+        g_free (self->unit_term);
 
         G_OBJECT_CLASS (gr_edit_page_parent_class)->finalize (object);
 }
@@ -217,18 +224,25 @@ ingredient_changed (GrEditPage *page)
 static void hide_ingredients_search_list (GrEditPage *self,
                                           gboolean    animate);
 
+static void hide_units_search_list (GrEditPage *self,
+                                    gboolean    animate);
+
 static void
 add_ingredient (GtkButton *button, GrEditPage *page)
 {
         gtk_entry_set_text (GTK_ENTRY (page->new_ingredient_name), "");
         gtk_entry_set_text (GTK_ENTRY (page->new_ingredient_amount), "");
-        gtk_entry_set_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (page->new_ingredient_unit))), "");
+        gtk_entry_set_text (GTK_ENTRY (page->new_ingredient_unit), "");
+        gtk_label_set_label (GTK_LABEL (page->ing_search_button_label), _("Ingredient"));
+        gtk_label_set_label (GTK_LABEL (page->amount_search_button_label), _("Amount"));
 
         gtk_popover_set_relative_to (GTK_POPOVER (page->ingredient_popover), GTK_WIDGET (button));
         gtk_button_set_label (GTK_BUTTON (page->new_ingredient_add_button), _("Add"));
         ingredient_changed (page);
 
         hide_ingredients_search_list (page, FALSE);
+        hide_units_search_list (page, FALSE);
+
         gtk_popover_popup (GTK_POPOVER (page->ingredient_popover));
 }
 
@@ -254,20 +268,27 @@ edit_ingredients_row (GtkListBoxRow *row,
         const char *amount;
         const char *unit;
         const char *ingredient;
+        char *tmp;
 
         amount = (const char *)g_object_get_data (G_OBJECT (row), "amount");
         unit = (const char *)g_object_get_data (G_OBJECT (row), "unit");
         ingredient = (const char *)g_object_get_data (G_OBJECT (row), "ingredient");
 
         gtk_entry_set_text (GTK_ENTRY (page->new_ingredient_name), ingredient);
-        gtk_label_set_label (GTK_LABEL (page->ing_search_button_label), ingredient);
         gtk_entry_set_text (GTK_ENTRY (page->new_ingredient_amount), amount);
-        gtk_entry_set_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (page->new_ingredient_unit))), unit);
+        gtk_entry_set_text (GTK_ENTRY (page->new_ingredient_unit), unit);
+        gtk_label_set_label (GTK_LABEL (page->ing_search_button_label), ingredient);
+        tmp = g_strconcat (amount, " ", unit, NULL);
+        gtk_label_set_label (GTK_LABEL (page->amount_search_button_label), tmp);
+        g_free (tmp);
 
         gtk_popover_set_relative_to (GTK_POPOVER (page->ingredient_popover), GTK_WIDGET (row));
         gtk_button_set_label (GTK_BUTTON (page->new_ingredient_add_button), _("Apply"));
         ingredient_changed (page);
+
         hide_ingredients_search_list (page, FALSE);
+        hide_units_search_list (page, FALSE);
+
         gtk_popover_popup (GTK_POPOVER (page->ingredient_popover));
 }
 
@@ -414,11 +435,29 @@ add_ingredient_row (GrEditPage   *page,
 }
 
 static void
+format_unit_for_display (const char *amount, const char *unit,
+                         char **amount_out, char **unit_out)
+{
+        g_autoptr(GrIngredientsList) ingredients = NULL;
+        g_autofree char *line = NULL;
+        g_autofree char *parsed = NULL;
+        g_auto(GStrv) strv = NULL;
+
+        line = g_strconcat (amount, " ", unit, " X", NULL);
+        ingredients = gr_ingredients_list_new (line);
+
+        parsed = gr_ingredients_list_scale_unit (ingredients, "", "X", 1, 1);
+        strv = g_strsplit (parsed, " ", 2);
+        *amount_out = g_strdup (strv[0]);
+        *unit_out = g_strdup (strv[1] ? strv[1] : "");
+}
+
+static void
 add_ingredient2 (GtkButton *button, GrEditPage *page)
 {
         const char *ingredient;
-        const char *amount;
-        const char *unit;
+        g_autofree char *amount = NULL;
+        g_autofree  char *unit = NULL;
         g_autofree char *s = NULL;
         GtkWidget *list;
         GtkWidget *b;
@@ -436,8 +475,9 @@ add_ingredient2 (GtkButton *button, GrEditPage *page)
         }
 
         ingredient = gtk_entry_get_text (GTK_ENTRY (page->new_ingredient_name));
-        amount = gtk_entry_get_text (GTK_ENTRY (page->new_ingredient_amount));
-        unit = gtk_entry_get_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (page->new_ingredient_unit))));
+        format_unit_for_display (gtk_entry_get_text (GTK_ENTRY (page->new_ingredient_amount)),
+                                 gtk_entry_get_text (GTK_ENTRY (page->new_ingredient_unit)),
+                                 &amount, &unit);
         update_ingredient_row (row, amount, unit, ingredient);
 }
 
@@ -517,6 +557,7 @@ static void
 ing_search_button_clicked (GtkButton  *button,
                            GrEditPage *self)
 {
+        hide_units_search_list (self, TRUE);
         show_ingredients_search_list (self);
 }
 
@@ -602,6 +643,155 @@ populate_ingredients_list (GrEditPage *self)
                           G_CALLBACK (ing_row_activated), self);
 }
 
+static void
+show_units_search_list (GrEditPage *self)
+{
+        gtk_widget_hide (self->amount_search_button);
+        gtk_widget_show (self->amount_search_revealer);
+        gtk_revealer_set_reveal_child (GTK_REVEALER (self->amount_search_revealer), TRUE);
+}
+
+static void
+hide_units_search_list (GrEditPage *self,
+                        gboolean    animate)
+{
+        gtk_widget_show (self->amount_search_button);
+        if (!animate)
+                gtk_revealer_set_transition_type (GTK_REVEALER (self->amount_search_revealer),
+                                                  GTK_REVEALER_TRANSITION_TYPE_NONE);
+        gtk_revealer_set_reveal_child (GTK_REVEALER (self->amount_search_revealer), FALSE);
+        if (!animate)
+                gtk_revealer_set_transition_type (GTK_REVEALER (self->amount_search_revealer),
+                                                  GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN);
+}
+
+static void
+amount_search_button_clicked (GtkButton  *button,
+                              GrEditPage *self)
+{
+        hide_ingredients_search_list (self, TRUE);
+        show_units_search_list (self);
+}
+
+static void
+unit_row_activated (GtkListBox    *list,
+                    GtkListBoxRow *row,
+                    GrEditPage *self)
+{
+        g_autofree char *unit = NULL;
+        g_autofree char *amount = NULL;
+        char *tmp;
+
+        format_unit_for_display (gtk_entry_get_text (GTK_ENTRY (self->new_ingredient_amount)),
+                                 (const char *) g_object_get_data (G_OBJECT (row), "unit"),
+                                 &amount, &unit);
+
+        gtk_entry_set_text (GTK_ENTRY (self->new_ingredient_unit), unit);
+        tmp = g_strconcat (amount, " ", unit, NULL);
+        gtk_label_set_label (GTK_LABEL (self->amount_search_button_label), tmp);
+        g_free (tmp);
+
+        hide_units_search_list (self, TRUE);
+}
+
+static gboolean
+unit_filter_func (GtkListBoxRow *row,
+                  gpointer       data)
+{
+        GrEditPage *self = data;
+        const char *cf;
+
+        if (!self->unit_term)
+                return TRUE;
+
+        cf = (const char *)g_object_get_data (G_OBJECT (row), "unit");
+
+        return g_str_has_prefix (cf, self->unit_term);
+}
+
+static void
+unit_filter_changed (GrEditPage *self)
+{
+        const char *term;
+
+        term = gtk_entry_get_text (GTK_ENTRY (self->new_ingredient_unit));
+        g_free (self->unit_term);
+        self->unit_term = g_utf8_casefold (term, -1);
+        gtk_list_box_invalidate_filter (GTK_LIST_BOX (self->unit_list));
+}
+
+static void
+unit_filter_stop (GrEditPage *self)
+{
+        gtk_entry_set_text (GTK_ENTRY (self->new_ingredient_unit), "");
+}
+
+static void
+unit_filter_activated (GrEditPage *self)
+{
+        const char *amount;
+        const char *unit;
+        g_autofree char *ings = NULL;
+        g_autofree char *parsed = NULL;
+        g_auto(GStrv) strv = NULL;
+        g_autoptr(GrIngredientsList) ingredients = NULL;
+
+        amount = gtk_entry_get_text (GTK_ENTRY (self->new_ingredient_amount));
+        unit = gtk_entry_get_text (GTK_ENTRY (self->new_ingredient_unit));
+
+        ings = g_strconcat (amount, " ", unit, " X", NULL);
+        ingredients = gr_ingredients_list_new (ings);
+        parsed = gr_ingredients_list_scale_unit (ingredients, "", "X", 1, 1);
+        strv = g_strsplit (parsed, " ", 2);
+        amount = strv[0];
+        unit = strv[1] ? strv[1] : "";
+
+        gtk_label_set_label (GTK_LABEL (self->amount_search_button_label), parsed);
+        hide_units_search_list (self, TRUE);
+}
+
+static void
+populate_units_list (GrEditPage *self)
+{
+        const char * const units[] = {
+                "kilogram",
+                "gram",
+                "pound",
+                "liter",
+                "ounze",
+                "package",
+                "piece",
+                "box",
+                "bag",
+                "teaspoon",
+                "tablespoon",
+                NULL
+        };
+        GtkWidget *row;
+        int i;
+
+        for (i = 0; units[i]; i++) {
+                row = gtk_label_new (units[i]);
+                g_object_set (row,
+                              "visible", TRUE,
+                              "margin", 10,
+                              "xalign", 0.0,
+                              NULL);
+                gtk_container_add (GTK_CONTAINER (self->unit_list), row);
+                row = gtk_widget_get_parent (row);
+
+                g_object_set_data_full (G_OBJECT (row), "unit", g_strdup (units[i]), g_free);
+        }
+
+        gtk_list_box_set_header_func (GTK_LIST_BOX (self->unit_list),
+                                      all_headers, self, NULL);
+
+        gtk_list_box_set_filter_func (GTK_LIST_BOX (self->unit_list),
+                                      unit_filter_func, self, NULL);
+
+        g_signal_connect (self->unit_list, "row-activated",
+                          G_CALLBACK (unit_row_activated), self);
+}
 
 static void
 gr_edit_page_init (GrEditPage *page)
@@ -615,6 +805,7 @@ gr_edit_page_init (GrEditPage *page)
         populate_category_combo (page);
         populate_season_combo (page);
         populate_ingredients_list (page);
+        populate_units_list (page);
 }
 
 static void
@@ -662,6 +853,11 @@ gr_edit_page_class_init (GrEditPageClass *klass)
         gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GrEditPage, ing_search_button_label);
         gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GrEditPage, ing_search_revealer);
 
+        gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GrEditPage, unit_list);
+        gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GrEditPage, amount_search_button);
+        gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GrEditPage, 
amount_search_button_label);
+        gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GrEditPage, amount_search_revealer);
+
         gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (klass), dismiss_error);
         gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (klass), add_image);
         gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (klass), remove_image);
@@ -676,6 +872,11 @@ gr_edit_page_class_init (GrEditPageClass *klass)
         gtk_widget_class_bind_template_callback (widget_class, ing_filter_stop);
         gtk_widget_class_bind_template_callback (widget_class, ing_filter_activated);
         gtk_widget_class_bind_template_callback (widget_class, ing_search_button_clicked);
+
+        gtk_widget_class_bind_template_callback (widget_class, unit_filter_changed);
+        gtk_widget_class_bind_template_callback (widget_class, unit_filter_stop);
+        gtk_widget_class_bind_template_callback (widget_class, unit_filter_activated);
+        gtk_widget_class_bind_template_callback (widget_class, amount_search_button_clicked);
 }
 
 GtkWidget *
diff --git a/src/gr-edit-page.ui b/src/gr-edit-page.ui
index a14be57..89b154c 100644
--- a/src/gr-edit-page.ui
+++ b/src/gr-edit-page.ui
@@ -715,25 +715,83 @@
           </object>
         </child>
         <child>
-          <object class="GtkBox">
+          <object class="GtkButton" id="amount_search_button">
             <property name="visible">1</property>
-            <property name="orientation">horizontal</property>
-            <style> <class name="linked"/> </style>
+            <signal name="clicked" handler="amount_search_button_clicked"/>
             <child>
-              <object class="GtkEntry" id="new_ingredient_amount">
+              <object class="GtkBox">
                 <property name="visible">1</property>
-                <property name="width-chars">6</property>
+                <property name="spacing">10</property>
+                <child>
+                  <object class="GtkLabel" id="amount_search_button_label">
+                    <property name="visible">1</property>
+                    <property name="xalign">0</property>
+                    <property name="ellipsize">end</property>
+                    <property name="max-width-chars">25</property>
+                    <property name="label" translatable="yes">Amount</property>
+                  </object>
+                  <packing>
+                    <property name="expand">1</property>
+                  </packing>
+                </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="GtkRevealer" id="amount_search_revealer">
+            <property name="visible">1</property>
             <child>
-              <object class="GtkComboBoxText" id="new_ingredient_unit">
+              <object class="GtkBox">
                 <property name="visible">1</property>
-                <property name="has-entry">1</property>
-                <items>
-                  <item id="g" translatable="yes">grams</item>
-                  <item id="kg" translatable="yes">kilograms</item>
-                  <item id="lb" translatable="yes">pounds</item>
-                </items>
+                <property name="orientation">vertical</property>
+                <property name="spacing">10</property>
+                <child>
+                  <object class="GtkBox">
+                    <property name="visible">1</property>
+                    <property name="orientation">horizontal</property>
+                    <style> <class name="linked"/> </style>
+                    <child>
+                      <object class="GtkEntry" id="new_ingredient_amount">
+                        <property name="visible">1</property>
+                        <property name="width-chars">6</property>
+                        <signal name="activate" handler="unit_filter_activated" swapped="yes"/>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkSearchEntry" id="new_ingredient_unit">
+                        <property name="visible">1</property>
+                        <property name="placeholder-text" translatable="yes">Search…</property>
+                        <signal name="search-changed" handler="unit_filter_changed" swapped="yes"/>
+                        <signal name="stop-search" handler="unit_filter_stop" swapped="yes"/>
+                        <signal name="activate" handler="unit_filter_activated" swapped="yes"/>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkScrolledWindow">
+                    <property name="visible">1</property>
+                    <property name="shadow-type">in</property>
+                    <property name="hscrollbar-policy">never</property>
+                    <property name="propagate-natural-height">1</property>
+                    <property name="max-content-height">220</property>
+                    <property name="height-request">220</property>
+                    <child>
+                      <object class="GtkListBox" id="unit_list">
+                        <property name="visible">1</property>
+                        <property name="selection-mode">none</property>
+                      </object>
+                    </child>
+                  </object>
+                </child>
               </object>
             </child>
           </object>


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