[recipes] Redo timer notifications
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [recipes] Redo timer notifications
- Date: Fri, 24 Feb 2017 05:23:49 +0000 (UTC)
commit c093695f0899191979b451e483852218506a3385
Author: Matthias Clasen <mclasen redhat com>
Date: Thu Feb 23 22:23:33 2017 -0500
Redo timer notifications
Arrange things so timers can outlive the cooking mode,
and emit a system notification in that case. Also add
a "Show" button to the in-app notification.
src/gr-cooking-page.c | 19 +++-
src/gr-cooking-page.h | 3 +-
src/gr-cooking-page.ui | 8 ++
src/gr-cooking-view.c | 300 +++++++++++++++++++++++++++++++-----------------
src/gr-cooking-view.h | 1 +
src/gr-edit-page.c | 2 +-
6 files changed, 222 insertions(+), 111 deletions(-)
---
diff --git a/src/gr-cooking-page.c b/src/gr-cooking-page.c
index 26c20d2..2fbb1bc 100644
--- a/src/gr-cooking-page.c
+++ b/src/gr-cooking-page.c
@@ -48,6 +48,7 @@ struct _GrCookingPage
GtkWidget *notification_revealer;
GtkWidget *notification_label;
GtkWidget *mini_timer_box;
+ int notification_step;
GrRecipe *recipe;
@@ -411,10 +412,12 @@ gr_cooking_page_handle_event (GrCookingPage *page,
void
gr_cooking_page_show_notification (GrCookingPage *page,
- const char *text)
+ const char *text,
+ int step)
{
gtk_label_set_label (GTK_LABEL (page->notification_label), text);
gtk_revealer_set_reveal_child (GTK_REVEALER (page->notification_revealer), TRUE);
+ page->notification_step = step;
}
static void
@@ -424,6 +427,13 @@ close_notification (GrCookingPage *page)
}
static void
+goto_timer (GrCookingPage *page)
+{
+ gtk_revealer_set_reveal_child (GTK_REVEALER (page->notification_revealer), FALSE);
+ gr_cooking_view_set_step (GR_COOKING_VIEW (page->cooking_view), page->notification_step);
+}
+
+static void
gr_cooking_page_class_init (GrCookingPageClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
@@ -453,6 +463,7 @@ gr_cooking_page_class_init (GrCookingPageClass *klass)
gtk_widget_class_bind_template_callback (widget_class, motion_notify);
gtk_widget_class_bind_template_callback (widget_class, close_notification);
gtk_widget_class_bind_template_callback (widget_class, on_child_revealed);
+ gtk_widget_class_bind_template_callback (widget_class, goto_timer);
}
void
@@ -460,14 +471,16 @@ gr_cooking_page_set_recipe (GrCookingPage *page,
GrRecipe *recipe)
{
g_autoptr(GArray) images = NULL;
- const char *instructions = NULL;
+ const char *id;
+ const char *instructions;
g_set_object (&page->recipe, recipe);
g_object_get (recipe, "images", &images, NULL);
+ id = gr_recipe_get_id (recipe);
instructions = gr_recipe_get_translated_instructions (recipe);
container_remove_all (GTK_CONTAINER (page->mini_timer_box));
- gr_cooking_view_set_data (GR_COOKING_VIEW (page->cooking_view), instructions, images);
+ gr_cooking_view_set_data (GR_COOKING_VIEW (page->cooking_view), id, instructions, images);
}
diff --git a/src/gr-cooking-page.h b/src/gr-cooking-page.h
index 0b3b2ee..4554d79 100644
--- a/src/gr-cooking-page.h
+++ b/src/gr-cooking-page.h
@@ -39,7 +39,8 @@ gboolean gr_cooking_page_handle_event (GrCookingPage *page,
GdkEvent *event);
void gr_cooking_page_show_notification (GrCookingPage *page,
- const char *text);
+ const char *text,
+ int step);
G_END_DECLS
diff --git a/src/gr-cooking-page.ui b/src/gr-cooking-page.ui
index 9d4fef9..45ac00f 100644
--- a/src/gr-cooking-page.ui
+++ b/src/gr-cooking-page.ui
@@ -73,6 +73,14 @@
<object class="GtkButton">
<property name="visible">1</property>
<property name="focus-on-click">0</property>
+ <property name="label" translatable="yes">Show</property>
+ <signal name="clicked" handler="goto_timer" 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_notification" swapped="yes"/>
<style>
diff --git a/src/gr-cooking-view.c b/src/gr-cooking-view.c
index a1d84ed..f3772fe 100644
--- a/src/gr-cooking-view.c
+++ b/src/gr-cooking-view.c
@@ -38,6 +38,34 @@
#include "gr-cooking-page.h"
#include "gr-timer-widget.h"
+struct _GrCookingView
+{
+ GtkBox parent_instance;
+
+ GtkWidget *cooking_heading;
+ GtkWidget *cooking_label;
+ GtkWidget *cooking_image;
+ GtkWidget *cooking_stack;
+ GtkWidget *cooking_timer;
+ GtkWidget *text_box;
+ GtkWidget *timer_box;
+
+ GArray *images;
+ char *id;
+ char *instructions;
+
+ GPtrArray *steps;
+ int step;
+
+ gboolean wide;
+
+#ifdef ENABLE_CANBERRA
+ ca_context *c;
+#endif
+
+ GList *timers;
+};
+
typedef struct
{
GrCookingView *view;
@@ -45,10 +73,10 @@ typedef struct
char *heading;
char *label;
GrTimer *timer;
- GtkWidget *mini_timer;
gulong handler;
guint64 duration;
int image;
+ GtkWidget *mini_timer;
} StepData;
static void
@@ -56,33 +84,77 @@ step_data_free (gpointer data)
{
StepData *d = data;
- if (d->timer)
+ if (d->handler)
g_signal_handler_disconnect (d->timer, d->handler);
+
g_clear_object (&d->timer);
g_free (d->heading);
g_free (d->label);
g_free (d);
}
-static void step_timer_complete (GrTimer *timer, StepData *step);
+typedef struct {
+ GrTimer *timer;
+ GrCookingView *view;
+ gboolean use_system;
+ gulong handler;
+ char *id;
+ int num;
+} TimerData;
+
+static void
+timer_data_free (gpointer data)
+{
+ TimerData *td = data;
+
+ if (td->handler)
+ g_signal_handler_disconnect (td->timer, td->handler);
+ g_free (td->id);
+
+ g_free (td);
+}
+
+static void timer_complete (GrTimer *timer, TimerData *data);
+static void go_to_step (StepData *data);
+
+static void
+timer_active (GrTimer *timer,
+ GParamSpec *pspec,
+ StepData *d)
+{
+ TimerData *td = g_object_get_data (G_OBJECT (timer), "timer-data");
+ GrCookingView *view = td->view;
+
+ if (d->mini_timer)
+ gtk_widget_show (d->mini_timer);
+
+ view->timers = g_list_prepend (view->timers, g_object_ref (timer));
+
+ g_signal_handler_disconnect (timer, d->handler);
+ d->handler = 0;
+
+ td->handler = g_signal_connect (d->timer, "complete",
+ G_CALLBACK (timer_complete), td);
+}
static StepData *
step_data_new (int num,
int n_steps,
- const char *label,
+ const char *text,
guint64 duration,
int image,
- gpointer view)
+ GrCookingView *view)
{
StepData *d;
- d = g_new (StepData, 1);
+ d = g_new0 (StepData, 1);
d->view = view;
d->num = num;
d->heading = g_strdup_printf (_("Step %d/%d"), num + 1, n_steps);
- d->label = g_strdup (label);
+ d->label = g_strdup (text);
if (duration > 0) {
g_autofree char *name = NULL;
+ TimerData *td;
name = g_strdup_printf (_("Step %d"), num + 1);
d->timer = g_object_new (GR_TYPE_TIMER,
@@ -90,42 +162,61 @@ step_data_new (int num,
"duration", duration,
"active", FALSE,
NULL);
- d->handler = g_signal_connect (d->timer, "complete", G_CALLBACK (step_timer_complete), d);
- }
- else {
- d->timer = NULL;
- d->handler = 0;
- }
- d->duration = duration;
- d->image = image;
- return d;
-}
+ td = g_new0 (TimerData, 1);
+ td->timer = d->timer;
+ td->view = view;
+ td->use_system = FALSE;
+ td->id = g_strdup (view->id);
+ td->num = num;
-struct _GrCookingView
-{
- GtkBox parent_instance;
+ g_object_set_data_full (G_OBJECT (d->timer), "timer-data", td, timer_data_free);
- GtkWidget *cooking_heading;
- GtkWidget *cooking_label;
- GtkWidget *cooking_image;
- GtkWidget *cooking_stack;
- GtkWidget *cooking_timer;
- GtkWidget *text_box;
- GtkWidget *timer_box;
+ d->handler = g_signal_connect (d->timer, "notify::active",
+ G_CALLBACK (timer_active), d);
- GArray *images;
- char *instructions;
+ if (view->timer_box) {
+ GtkWidget *button;
+ GtkWidget *box;
+ GtkWidget *tw;
+ GtkWidget *label;
- GPtrArray *steps;
- int step;
+ button = gtk_button_new ();
+ gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+ gtk_widget_set_focus_on_click (button, FALSE);
+ gtk_style_context_add_class (gtk_widget_get_style_context (button), "osd");
+ g_signal_connect_swapped (button, "clicked", G_CALLBACK (go_to_step), d);
- gboolean wide;
+ box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
+ gtk_widget_show (box);
+ tw = g_object_new (GR_TYPE_TIMER_WIDGET,
+ "timer", d->timer,
+ "size", 32,
+ "visible", TRUE,
+ NULL);
+ label = gtk_label_new (name);
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_widget_show (label);
+ gtk_style_context_add_class (gtk_widget_get_style_context (label),
"cooking-heading");
-#ifdef ENABLE_CANBERRA
- ca_context *c;
-#endif
-};
+ gtk_container_add (GTK_CONTAINER (box), tw);
+ gtk_container_add (GTK_CONTAINER (box), label);
+ gtk_container_add (GTK_CONTAINER (button), box);
+ gtk_container_add (GTK_CONTAINER (view->timer_box), button);
+
+ g_signal_connect_object (d->timer, "complete",
+ G_CALLBACK (gtk_widget_destroy), button,
+ G_CONNECT_SWAPPED);
+
+ d->mini_timer = button;
+ }
+ }
+
+ d->duration = duration;
+ d->image = image;
+
+ return d;
+}
G_DEFINE_TYPE (GrCookingView, gr_cooking_view, GTK_TYPE_BOX)
@@ -155,6 +246,8 @@ gr_cooking_view_finalize (GObject *object)
ca_context_destroy (self->c);
#endif
+ g_list_free_full (self->timers, g_object_unref);
+
G_OBJECT_CLASS (gr_cooking_view_parent_class)->finalize (object);
}
@@ -240,10 +333,31 @@ setup_step (GrCookingView *view)
}
static void
-play_complete_sound (StepData *step)
+set_step (GrCookingView *view,
+ int step)
+{
+ if (step < 0)
+ step = 0;
+ else if (step >= view->steps->len)
+ step = view->steps->len - 1;
+
+ if (view->step != step) {
+ view->step = step;
+ setup_step (view);
+ }
+}
+
+static void
+go_to_step (StepData *data)
+{
+ set_step (data->view, data->num);
+}
+
+static void
+play_complete_sound (TimerData *td)
{
#ifdef ENABLE_CANBERRA
- GrCookingView *view = step->view;
+ GrCookingView *view = td->view;
GtkWidget *page;
page = gtk_widget_get_ancestor (GTK_WIDGET (view), GR_TYPE_COOKING_PAGE);
@@ -261,9 +375,10 @@ play_complete_sound (StepData *step)
}
static void
-send_complete_notification (StepData *step)
+show_complete_notification (GrTimer *timer,
+ TimerData *td)
{
- GrCookingView *view = step->view;
+ GrCookingView *view = td->view;
GtkWidget *page;
page = gtk_widget_get_ancestor (GTK_WIDGET (view), GR_TYPE_COOKING_PAGE);
@@ -271,27 +386,42 @@ send_complete_notification (StepData *step)
g_autofree char *text = NULL;
text = g_strdup_printf (_("Timer for ā%sā has expired."),
- gr_timer_get_name (step->timer));
- gr_cooking_page_show_notification (GR_COOKING_PAGE (page), text);
+ gr_timer_get_name (timer));
+ gr_cooking_page_show_notification (GR_COOKING_PAGE (page), text, td->num);
}
}
static void
-step_timer_complete (GrTimer *timer, StepData *step)
+timer_complete (GrTimer *timer,
+ TimerData *td)
{
- GrCookingView *view = step->view;
- StepData *current;
+ GrCookingView *view = td->view;
- current = g_ptr_array_index (view->steps, view->step);
- if (step == current)
- gtk_stack_set_visible_child_name (GTK_STACK (view->cooking_stack), "complete");
- else
- send_complete_notification (step);
+ if (td->use_system) {
+ GApplication *app = g_application_get_default ();
+ g_autoptr(GNotification) notification = NULL;
+ g_autofree char *body = NULL;
- if (step->mini_timer)
- gtk_widget_destroy (step->mini_timer);
+ notification = g_notification_new (_("Timer is up!"));
- play_complete_sound (step);
+ body = g_strdup_printf (_("The timer for '%s' (recipe %s) has expired."), gr_timer_get_name
(timer), td->id);
+ g_notification_set_body (notification, body);
+ g_application_send_notification (app, "timer", notification);
+ }
+ else {
+ if (view->step == td->num)
+ gtk_stack_set_visible_child_name (GTK_STACK (view->cooking_stack), "complete");
+ else
+ show_complete_notification (timer, td);
+ }
+
+ play_complete_sound (td);
+
+ g_signal_handler_disconnect (timer, td->handler);
+ td->handler = 0;
+
+ view->timers = g_list_remove (view->timers, timer);
+ g_object_unref (timer);
}
static void
@@ -329,21 +459,6 @@ step_timer_reset (GrCookingView *view)
}
static void
-set_step (GrCookingView *view,
- int step)
-{
- if (step < 0)
- step = 0;
- else if (step >= view->steps->len)
- step = view->steps->len - 1;
-
- if (view->step != step) {
- view->step = step;
- setup_step (view);
- }
-}
-
-static void
gr_cooking_view_get_property (GObject *object,
guint prop_id,
GValue *value,
@@ -427,12 +542,6 @@ gr_cooking_view_class_init (GrCookingViewClass *klass)
}
static void
-go_to_step (StepData *data)
-{
- set_step (data->view, data->num);
-}
-
-static void
setup_steps (GrCookingView *view)
{
g_autoptr(GPtrArray) steps = NULL;
@@ -454,46 +563,17 @@ setup_steps (GrCookingView *view)
data = step_data_new (i, steps->len, step->text, step->timer, step->image, view);
g_ptr_array_add (view->steps, data);
- if (view->timer_box && data->timer) {
- GtkWidget *button;
- GtkWidget *box;
- GtkWidget *tw;
- GtkWidget *label;
-
- button = gtk_button_new ();
- gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
- gtk_widget_set_focus_on_click (button, FALSE);
- gtk_style_context_add_class (gtk_widget_get_style_context (button), "osd");
- g_signal_connect_swapped (button, "clicked", G_CALLBACK (go_to_step), data);
-
- box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
- gtk_widget_show (box);
- tw = g_object_new (GR_TYPE_TIMER_WIDGET,
- "timer", data->timer,
- "size", 32,
- "visible", TRUE,
- NULL);
- label = gtk_label_new (gr_timer_get_name (data->timer));
- gtk_label_set_xalign (GTK_LABEL (label), 0.0);
- gtk_widget_show (label);
- gtk_style_context_add_class (gtk_widget_get_style_context (label),
"cooking-heading");
-
- gtk_container_add (GTK_CONTAINER (box), tw);
- gtk_container_add (GTK_CONTAINER (box), label);
- gtk_container_add (GTK_CONTAINER (button), box);
- gtk_container_add (GTK_CONTAINER (view->timer_box), button);
- g_signal_connect_swapped (data->timer, "notify::active", G_CALLBACK
(gtk_widget_show), button);
-
- data->mini_timer = button;
- }
}
}
void
gr_cooking_view_set_data (GrCookingView *view,
+ const char *id,
const char *instructions,
GArray *images)
{
+ g_free (view->id);
+ view->id = g_strdup (id);
g_free (view->instructions);
view->instructions = g_strdup (instructions);
g_clear_pointer (&view->images, g_array_unref);
@@ -530,6 +610,8 @@ gr_cooking_view_start (GrCookingView *view)
void
gr_cooking_view_stop (GrCookingView *view)
{
+ GList *l;
+
g_object_set (view->cooking_timer, "timer", NULL, NULL);
if (view->timer_box)
container_remove_all (GTK_CONTAINER (view->timer_box));
@@ -537,6 +619,12 @@ gr_cooking_view_stop (GrCookingView *view)
g_clear_pointer (&view->instructions, g_free);
g_clear_pointer (&view->images, g_array_unref);
g_ptr_array_set_size (view->steps, 0);
+
+ for (l = view->timers; l; l = l->next) {
+ GrTimer *timer = l->data;
+ TimerData *td = g_object_get_data (G_OBJECT (timer), "timer-data");
+ td->use_system = TRUE;
+ }
}
void
diff --git a/src/gr-cooking-view.h b/src/gr-cooking-view.h
index e37d0fe..b76a7a6 100644
--- a/src/gr-cooking-view.h
+++ b/src/gr-cooking-view.h
@@ -32,6 +32,7 @@ G_DECLARE_FINAL_TYPE (GrCookingView, gr_cooking_view, GR, COOKING_VIEW, GtkBox)
GrCookingView *gr_cooking_view_new (void);
void gr_cooking_view_set_data (GrCookingView *view,
+ const char *recipe_id,
const char *instructions,
GArray *images);
int gr_cooking_view_get_n_steps (GrCookingView *view);
diff --git a/src/gr-edit-page.c b/src/gr-edit-page.c
index 4865e89..21664bb 100644
--- a/src/gr-edit-page.c
+++ b/src/gr-edit-page.c
@@ -1379,7 +1379,7 @@ preview_visible_changed (GrEditPage *page)
g_object_get (page->images, "images", &images, NULL);
instructions = get_text_view_text (GTK_TEXT_VIEW (page->instructions_field));
- gr_cooking_view_set_data (GR_COOKING_VIEW (page->cooking_view), instructions, images);
+ gr_cooking_view_set_data (GR_COOKING_VIEW (page->cooking_view), NULL, instructions, images);
gr_cooking_view_start (GR_COOKING_VIEW (page->cooking_view));
update_steppers (page);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]