[recipes] Add an undo notification for deleting recipes



commit 7ca7115dfcc00e83432f1a3c61797dadd113e6fc
Author: Matthias Clasen <mclasen redhat com>
Date:   Mon Jan 9 21:33:19 2017 -0500

    Add an undo notification for deleting recipes
    
    The implementation is inspired by the corresponding feature
    in nautilus. We show the notification for 10 seconds.

 src/gr-details-page.c |    5 ++
 src/gr-window.c       |   66 ++++++++++++++++++++
 src/gr-window.h       |    2 +
 src/gr-window.ui      |  160 +++++++++++++++++++++++++++++++++---------------
 4 files changed, 183 insertions(+), 50 deletions(-)
---
diff --git a/src/gr-details-page.c b/src/gr-details-page.c
index 1da9969..d85a349 100644
--- a/src/gr-details-page.c
+++ b/src/gr-details-page.c
@@ -246,8 +246,11 @@ static void
 delete_recipe (GrDetailsPage *page)
 {
         GrRecipeStore *store;
+        g_autoptr(GrRecipe) recipe = NULL;
         GtkWidget *window;
 
+        recipe = g_object_ref (page->recipe);
+
         store = gr_app_get_recipe_store (GR_APP (g_application_get_default ()));
         gr_recipe_store_remove_recipe (store, page->recipe);
         g_set_object (&page->recipe, NULL);
@@ -255,6 +258,8 @@ delete_recipe (GrDetailsPage *page)
 
         window = gtk_widget_get_ancestor (GTK_WIDGET (page), GTK_TYPE_APPLICATION_WINDOW);
         gr_window_go_back (GR_WINDOW (window));
+
+        gr_window_offer_undelete (GR_WINDOW (window), recipe);
 }
 
 static void
diff --git a/src/gr-window.c b/src/gr-window.c
index ab782c6..c58c8e5 100644
--- a/src/gr-window.c
+++ b/src/gr-window.c
@@ -57,6 +57,10 @@ struct _GrWindow
         GtkWidget *cuisines_page;
         GtkWidget *cuisine_page;
         GtkWidget *ingredients_page;
+        GtkWidget *undo_revealer;
+        GtkWidget *undo_label;
+        GrRecipe  *undo_recipe;
+        guint undo_timeout_id;
 
         GrRecipeImporter *importer;
 
@@ -343,10 +347,68 @@ gr_window_finalize (GObject *object)
 
         g_clear_object (&self->importer);
 
+        if (self->undo_timeout_id) {
+                g_source_remove (self->undo_timeout_id);
+                self->undo_timeout_id = 0;
+        }
+
         G_OBJECT_CLASS (gr_window_parent_class)->finalize (object);
 }
 
 static void
+close_undo (GrWindow *window)
+{
+        if (window->undo_timeout_id) {
+                g_source_remove (window->undo_timeout_id);
+                window->undo_timeout_id = 0;
+        }
+
+        g_clear_object (&window->undo_recipe);
+
+        gtk_revealer_set_reveal_child (GTK_REVEALER (window->undo_revealer), FALSE);
+}
+
+static void
+do_undo (GrWindow *window)
+{
+        GrRecipeStore *store;
+        g_autoptr(GrRecipe) recipe = NULL;
+
+        recipe = g_object_ref (window->undo_recipe);
+
+        store = gr_app_get_recipe_store (GR_APP (g_application_get_default ()));
+
+        gr_recipe_store_add_recipe (store, window->undo_recipe, NULL);
+        close_undo (window);
+
+        gr_window_show_recipe (window, recipe);
+}
+
+static gboolean
+undo_timeout (gpointer data)
+{
+        GrWindow *window = data;
+
+        close_undo (window);
+
+        return G_SOURCE_REMOVE;
+}
+
+void
+gr_window_offer_undelete (GrWindow *window,
+                          GrRecipe *recipe)
+{
+        g_autofree char *tmp = NULL;
+
+        window->undo_recipe = g_object_ref (recipe);
+        tmp = g_strdup_printf (_("Recipe ā€œ%sā€ deleted"), gr_recipe_get_name (recipe));
+        gtk_label_set_label (GTK_LABEL (window->undo_label), tmp);
+
+        gtk_revealer_set_reveal_child (GTK_REVEALER (window->undo_revealer), TRUE);
+        window->undo_timeout_id = g_timeout_add_seconds (10, undo_timeout, window);
+}
+
+static void
 gr_window_class_init (GrWindowClass *klass)
 {
         GObjectClass *object_class = G_OBJECT_CLASS (klass);
@@ -372,6 +434,8 @@ gr_window_class_init (GrWindowClass *klass)
         gtk_widget_class_bind_template_child (widget_class, GrWindow, cuisines_page);
         gtk_widget_class_bind_template_child (widget_class, GrWindow, cuisine_page);
         gtk_widget_class_bind_template_child (widget_class, GrWindow, ingredients_page);
+        gtk_widget_class_bind_template_child (widget_class, GrWindow, undo_revealer);
+        gtk_widget_class_bind_template_child (widget_class, GrWindow, undo_label);
 
         gtk_widget_class_bind_template_callback (widget_class, new_recipe);
         gtk_widget_class_bind_template_callback (widget_class, go_back);
@@ -383,6 +447,8 @@ gr_window_class_init (GrWindowClass *klass)
         gtk_widget_class_bind_template_callback (widget_class, stop_search);
         gtk_widget_class_bind_template_callback (widget_class, window_keypress_handler);
         gtk_widget_class_bind_template_callback (widget_class, window_mapped_handler);
+        gtk_widget_class_bind_template_callback (widget_class, do_undo);
+        gtk_widget_class_bind_template_callback (widget_class, close_undo);
 }
 
 static void
diff --git a/src/gr-window.h b/src/gr-window.h
index 51ee8ea..990cbe2 100644
--- a/src/gr-window.h
+++ b/src/gr-window.h
@@ -59,5 +59,7 @@ void            gr_window_show_season                (GrWindow   *window,
                                                       const char *title);
 void            gr_window_show_search_by_ingredients (GrWindow   *window,
                                                       const char *ingredient);
+void            gr_window_offer_undelete             (GrWindow   *window,
+                                                      GrRecipe   *recipe);
 
 G_END_DECLS
diff --git a/src/gr-window.ui b/src/gr-window.ui
index 2a9829e..7d2ae69 100644
--- a/src/gr-window.ui
+++ b/src/gr-window.ui
@@ -162,59 +162,119 @@
           </object>
         </child>
         <child>
-          <object class="GtkStack" id="main_stack">
+          <object class="GtkOverlay">
             <property name="visible">1</property>
-            <signal name="notify::visible-child-name" handler="visible_page_changed" swapped="yes"/>
-            <child>
-              <object class="GrRecipesPage" id="recipes_page"/>
-              <packing>
-                <property name="name">recipes</property>
-                <property name="title" translatable="yes">Recipes</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GrListPage" id="list_page"/>
-              <packing>
-                <property name="name">list</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GrSearchPage" id="search_page"/>
-              <packing>
-                <property name="name">search</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GrCuisinesPage" id="cuisines_page"/>
-              <packing>
-                <property name="name">cuisines</property>
-                <property name="title" translatable="yes">Cuisines</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GrCuisinePage" id="cuisine_page"/>
-              <packing>
-                <property name="name">cuisine</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GrIngredientsPage" id="ingredients_page"/>
-              <packing>
-                <property name="name">ingredients</property>
-                <property name="title" translatable="yes">Ingredients</property>
-              </packing>
-            </child>
-            <child>
-              <object class="GrDetailsPage" id="details_page"/>
-              <packing>
-                <property name="name">details</property>
-              </packing>
+            <child type="overlay">
+              <object class="GtkRevealer" id="undo_revealer">
+                <property name="visible">1</property>
+                <property name="halign">center</property>
+                <property name="valign">start</property>
+                <child>
+                  <object class="GtkFrame">
+                    <property name="visible">1</property>
+                    <style>
+                      <class name="app-notification"/>
+                    </style>
+                    <child>
+                      <object class="GtkBox">
+                        <property name="visible">1</property>
+                        <property name="spacing">20</property>
+                        <property name="margin">10</property>
+                        <child>
+                          <object class="GtkLabel" id="undo_label">
+                            <property name="visible">1</property>
+                            <property name="hexpand">1</property>
+                            <property name="halign">start</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkButton">
+                            <property name="visible">1</property>
+                            <property name="focus-on-click">0</property>
+                            <property name="label" translatable="yes">Undo</property>
+                            <signal name="clicked" handler="do_undo" swapped="yes"/>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkButton">
+                            <property name="visible">1</property>
+                            <property name="focus-on-click">0</property>
+                            <property name="relief">none</property>
+                            <signal name="clicked" handler="close_undo" swapped="yes"/>
+                            <style>
+                              <class name="image-button"/>
+                            </style>
+                            <child>
+                              <object class="GtkImage">
+                                <property name="visible">1</property>
+                                <property name="icon-name">window-close-symbolic</property>
+                                <property name="icon-size">1</property>
+                              </object>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
             </child>
             <child>
-              <object class="GrEditPage" id="edit_page"/>
-              <packing>
-                <property name="name">edit</property>
-              </packing>
+              <object class="GtkStack" id="main_stack">
+                <property name="visible">1</property>
+                <signal name="notify::visible-child-name" handler="visible_page_changed" swapped="yes"/>
+                <child>
+                  <object class="GrRecipesPage" id="recipes_page"/>
+                  <packing>
+                    <property name="name">recipes</property>
+                    <property name="title" translatable="yes">Recipes</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GrListPage" id="list_page"/>
+                  <packing>
+                    <property name="name">list</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GrSearchPage" id="search_page"/>
+                  <packing>
+                    <property name="name">search</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GrCuisinesPage" id="cuisines_page"/>
+                  <packing>
+                    <property name="name">cuisines</property>
+                    <property name="title" translatable="yes">Cuisines</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GrCuisinePage" id="cuisine_page"/>
+                  <packing>
+                    <property name="name">cuisine</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GrIngredientsPage" id="ingredients_page"/>
+                  <packing>
+                    <property name="name">ingredients</property>
+                    <property name="title" translatable="yes">Ingredients</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GrDetailsPage" id="details_page"/>
+                  <packing>
+                    <property name="name">details</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GrEditPage" id="edit_page"/>
+                  <packing>
+                    <property name="name">edit</property>
+                  </packing>
+                </child>
+              </object>
             </child>
           </object>
         </child>


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