[gnome-todo] new-task-row: add popover to select task list



commit 50f3fe5b255ed49ceba75119b499dd364dbda387
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date:   Sat Apr 22 11:02:55 2017 -0300

    new-task-row: add popover to select task list
    
    Some views, such as the Today and the Scheduled views,
    are not tied to any particular task list. Until now, we
    only assumed the default one, and silently added the new
    tasks in there.
    
    This commit adds a way to select the task list in those
    views.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=773839

 data/ui/new-task-row.ui  |   62 ++++++++++++-
 src/gtd-new-task-row.c   |  224 +++++++++++++++++++++++++++++++++++++++++++++-
 src/gtd-new-task-row.h   |    3 +
 src/gtd-task-list-view.c |   15 ++-
 4 files changed, 293 insertions(+), 11 deletions(-)
---
diff --git a/data/ui/new-task-row.ui b/data/ui/new-task-row.ui
index 806f01b..d205ece 100644
--- a/data/ui/new-task-row.ui
+++ b/data/ui/new-task-row.ui
@@ -47,10 +47,48 @@
               </packing>
             </child>
             <child>
-              <object class="GtkEntry" id="entry">
+              <object class="GtkBox">
                 <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <signal name="activate" handler="entry_activated_cb" object="GtdNewTaskRow" swapped="yes" />
+                <property name="can_focus">False</property>
+                <style>
+                  <class name="linked" />
+                </style>
+                <child>
+                  <object class="GtkEntry" id="entry">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="hexpand">True</property>
+                    <signal name="activate" handler="entry_activated_cb" object="GtdNewTaskRow" 
swapped="yes" />
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkMenuButton" id="list_selector_button">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="popover">tasklist_popover</property>
+                    <child>
+                      <object class="GtkBox">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="spacing">6</property>
+                        <child>
+                          <object class="GtkImage" id="list_color_icon">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkLabel" id="list_name_label">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="max_width_chars">20</property>
+                            <property name="ellipsize">end</property>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
               </object>
               <packing>
                 <property name="name">entry</property>
@@ -62,4 +100,22 @@
       </object>
     </child>
   </template>
+  <object class="GtkPopover" id="tasklist_popover">
+    <property name="can_focus">False</property>
+    <child>
+      <object class="GtkListBox" id="tasklist_list">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="margin">12</property>
+        <property name="selection_mode">none</property>
+        <signal name="row-activated" handler="tasklist_selected_cb" object="GtdNewTaskRow" swapped="no" />
+        <style>
+          <class name="background" />
+        </style>
+      </object>
+    </child>
+  </object>
+  <object class="GtkSizeGroup" id="sizegroup">
+    <property name="mode">horizontal</property>
+  </object>
 </interface>
diff --git a/src/gtd-new-task-row.c b/src/gtd-new-task-row.c
index e0bc40e..2a32ae7 100644
--- a/src/gtd-new-task-row.c
+++ b/src/gtd-new-task-row.c
@@ -16,15 +16,30 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include "gtd-manager.h"
 #include "gtd-new-task-row.h"
+#include "gtd-provider.h"
 #include "gtd-task.h"
+#include "gtd-task-list.h"
+
+#include <math.h>
 
 struct _GtdNewTaskRow
 {
   GtkListBoxRow       parent;
 
   GtkEntry           *entry;
+  GtkImage           *list_color_icon;
+  GtkLabel           *list_name_label;
+  GtkWidget          *list_selector_button;
+  GtkSizeGroup       *sizegroup;
   GtkStack           *stack;
+  GtkListBox         *tasklist_list;
+  GtkPopover         *tasklist_popover;
+
+  GtdTaskList        *selected_tasklist;
+
+  GtdManager         *manager;
 };
 
 G_DEFINE_TYPE (GtdNewTaskRow, gtd_new_task_row, GTK_TYPE_LIST_BOX_ROW)
@@ -60,11 +75,66 @@ gtd_new_task_row_focus_in_event (GtkWidget     *widget,
   return GDK_EVENT_PROPAGATE;
 }
 
+static cairo_surface_t*
+get_circle_surface_from_color (GdkRGBA *color,
+                               gint     size)
+{
+  cairo_surface_t *surface;
+  cairo_t *cr;
+
+  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, size, size);
+  cr = cairo_create (surface);
+
+  cairo_set_source_rgba (cr,
+                         color->red,
+                         color->green,
+                         color->blue,
+                         color->alpha);
+  cairo_arc (cr, size / 2.0, size / 2.0, size / 2.0, 0., 2 * M_PI);
+  cairo_fill (cr);
+  cairo_destroy (cr);
+
+  return surface;
+}
+
+static void
+set_selected_tasklist (GtdNewTaskRow *self,
+                       GtdTaskList   *list)
+{
+  cairo_surface_t *surface;
+  GtdManager *manager;
+  GdkRGBA *color;
+
+  manager = gtd_manager_get_default ();
+
+  /* NULL list means the default */
+  if (!list)
+    list = gtd_manager_get_default_task_list (manager);
+
+  if (!g_set_object (&self->selected_tasklist, list))
+    return;
+
+  color = gtd_task_list_get_color (list);
+  surface = get_circle_surface_from_color (color, 12);
+
+  gtk_image_set_from_surface (self->list_color_icon, surface);
+  gtk_label_set_label (self->list_name_label, gtd_task_list_get_name (list));
+
+  cairo_surface_destroy (surface);
+  gdk_rgba_free (color);
+}
+
 /*
  * Callbacks
  */
 
 static void
+default_tasklist_changed_cb (GtdNewTaskRow *self)
+{
+  set_selected_tasklist (self, NULL);
+}
+
+static void
 entry_activated_cb (GtdNewTaskRow *self)
 {
   GtdTask *new_task;
@@ -77,12 +147,91 @@ entry_activated_cb (GtdNewTaskRow *self)
   gtd_task_set_title (new_task, gtk_entry_get_text (self->entry));
   gtd_task_save (new_task);
 
-  g_signal_emit (self, signals[CREATE_TASK], 0, new_task);
+  g_signal_emit (self, signals[CREATE_TASK], 0, new_task, self->selected_tasklist);
 
   gtk_entry_set_text (self->entry, "");
 }
 
 static void
+tasklist_selected_cb (GtkListBox    *listbox,
+                      GtkListBoxRow *row,
+                      GtdNewTaskRow *self)
+{
+  GtdTaskList *list;
+
+  list = g_object_get_data (G_OBJECT (row), "tasklist");
+
+  set_selected_tasklist (self, list);
+
+  gtk_popover_popdown (self->tasklist_popover);
+  gtk_entry_grab_focus_without_selecting (self->entry);
+}
+
+static void
+update_tasklists_cb (GtdNewTaskRow *self)
+{
+  GList *tasklists, *l;
+
+  gtk_container_foreach (GTK_CONTAINER (self->tasklist_list),
+                         (GtkCallback) gtk_widget_destroy,
+                         NULL);
+
+  tasklists = gtd_manager_get_task_lists (self->manager);
+
+  for (l = tasklists; l != NULL; l = l->next)
+    {
+      GtkWidget *row, *box, *icon, *name, *provider;
+      cairo_surface_t *surface;
+      GdkRGBA *color;
+
+      box = g_object_new (GTK_TYPE_BOX,
+                          "orientation", GTK_ORIENTATION_HORIZONTAL,
+                          "spacing", 12,
+                          "margin", 6,
+                          NULL);
+
+      /* Icon */
+      color = gtd_task_list_get_color (l->data);
+      surface = get_circle_surface_from_color (color, 12);
+      icon = gtk_image_new_from_surface (surface);
+
+      gtk_container_add (GTK_CONTAINER (box), icon);
+
+      /* Tasklist name */
+      name = g_object_new (GTK_TYPE_LABEL,
+                           "label", gtd_task_list_get_name (l->data),
+                           "xalign", 0.0,
+                           "hexpand", TRUE,
+                           NULL);
+
+      gtk_container_add (GTK_CONTAINER (box), name);
+
+      /* Provider name */
+      provider = g_object_new (GTK_TYPE_LABEL,
+                               "label", gtd_provider_get_description (gtd_task_list_get_provider (l->data)),
+                               "xalign", 0.0,
+                               NULL);
+      gtk_style_context_add_class (gtk_widget_get_style_context (provider), "dim-label");
+      gtk_size_group_add_widget (self->sizegroup, provider);
+      gtk_container_add (GTK_CONTAINER (box), provider);
+
+      /* The row itself */
+      row = gtk_list_box_row_new ();
+      gtk_container_add (GTK_CONTAINER (row), box);
+      gtk_container_add (GTK_CONTAINER (self->tasklist_list), row);
+
+      g_object_set_data (G_OBJECT (row), "tasklist", l->data);
+
+      gtk_widget_show_all (row);
+
+      cairo_surface_destroy (surface);
+      gdk_rgba_free (color);
+    }
+
+  g_list_free (tasklists);
+}
+
+static void
 gtd_new_task_row_get_property (GObject    *object,
                                guint       prop_id,
                                GValue     *value,
@@ -101,11 +250,35 @@ gtd_new_task_row_set_property (GObject      *object,
 }
 
 static void
+gtd_new_task_row_dispose (GObject *object)
+{
+  GtdNewTaskRow *self = GTD_NEW_TASK_ROW (object);
+
+  if (self->manager)
+    {
+      g_signal_handlers_disconnect_by_func (self->manager,
+                                            update_tasklists_cb,
+                                            self);
+
+      g_signal_handlers_disconnect_by_func (self->manager,
+                                            default_tasklist_changed_cb,
+                                            self);
+
+      self->manager = NULL;
+    }
+
+  g_clear_object (&self->selected_tasklist);
+
+  G_OBJECT_CLASS (gtd_new_task_row_parent_class)->dispose (object);
+}
+
+static void
 gtd_new_task_row_class_init (GtdNewTaskRowClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
+  object_class->dispose = gtd_new_task_row_dispose;
   object_class->get_property = gtd_new_task_row_get_property;
   object_class->set_property = gtd_new_task_row_set_property;
 
@@ -145,6 +318,8 @@ gtd_new_task_row_class_init (GtdNewTaskRowClass *klass)
    * GtdNewTaskRow::create-task:
    *
    * Emitted when the row wants the parent widget to create a new task.
+   * If the task list is %NULL, assume the default task list of the
+   * default provider.
    */
   signals[CREATE_TASK] = g_signal_new ("create-task",
                                        GTD_TYPE_NEW_TASK_ROW,
@@ -154,15 +329,23 @@ gtd_new_task_row_class_init (GtdNewTaskRowClass *klass)
                                        NULL,
                                        NULL,
                                        G_TYPE_NONE,
-                                       1,
-                                       GTD_TYPE_TASK);
+                                       2,
+                                       GTD_TYPE_TASK,
+                                       GTD_TYPE_TASK_LIST);
 
   gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/todo/ui/new-task-row.ui");
 
   gtk_widget_class_bind_template_child (widget_class, GtdNewTaskRow, entry);
+  gtk_widget_class_bind_template_child (widget_class, GtdNewTaskRow, list_color_icon);
+  gtk_widget_class_bind_template_child (widget_class, GtdNewTaskRow, list_name_label);
+  gtk_widget_class_bind_template_child (widget_class, GtdNewTaskRow, list_selector_button);
+  gtk_widget_class_bind_template_child (widget_class, GtdNewTaskRow, sizegroup);
   gtk_widget_class_bind_template_child (widget_class, GtdNewTaskRow, stack);
+  gtk_widget_class_bind_template_child (widget_class, GtdNewTaskRow, tasklist_list);
+  gtk_widget_class_bind_template_child (widget_class, GtdNewTaskRow, tasklist_popover);
 
   gtk_widget_class_bind_template_callback (widget_class, entry_activated_cb);
+  gtk_widget_class_bind_template_callback (widget_class, tasklist_selected_cb);
 
   gtk_widget_class_set_css_name (widget_class, "taskrow");
 }
@@ -170,7 +353,33 @@ gtd_new_task_row_class_init (GtdNewTaskRowClass *klass)
 static void
 gtd_new_task_row_init (GtdNewTaskRow *self)
 {
+  GtdManager *manager = gtd_manager_get_default ();
+
   gtk_widget_init_template (GTK_WIDGET (self));
+
+  g_signal_connect_swapped (manager,
+                            "list-added",
+                            G_CALLBACK (update_tasklists_cb),
+                            self);
+
+  g_signal_connect_swapped (manager,
+                            "list-changed",
+                            G_CALLBACK (update_tasklists_cb),
+                            self);
+
+  g_signal_connect_swapped (manager,
+                            "list-removed",
+                            G_CALLBACK (update_tasklists_cb),
+                            self);
+
+  g_signal_connect_swapped (manager,
+                            "notify::default-task-list",
+                            G_CALLBACK (default_tasklist_changed_cb),
+                            self);
+
+  self->manager = manager;
+
+  set_selected_tasklist (self, NULL);
 }
 
 GtkWidget*
@@ -205,3 +414,12 @@ gtd_new_task_row_set_active (GtdNewTaskRow *self,
       gtk_widget_grab_focus (GTK_WIDGET (self->entry));
     }
 }
+
+void
+gtd_new_task_row_set_show_list_selector (GtdNewTaskRow *self,
+                                         gboolean       show_list_selector)
+{
+  g_return_if_fail (GTD_IS_NEW_TASK_ROW (self));
+
+  gtk_widget_set_visible (self->list_selector_button, show_list_selector);
+}
diff --git a/src/gtd-new-task-row.h b/src/gtd-new-task-row.h
index 0b96933..d64909c 100644
--- a/src/gtd-new-task-row.h
+++ b/src/gtd-new-task-row.h
@@ -34,6 +34,9 @@ gboolean             gtd_new_task_row_get_active                 (GtdNewTaskRow
 void                 gtd_new_task_row_set_active                 (GtdNewTaskRow      *self,
                                                                   gboolean            active);
 
+void                 gtd_new_task_row_set_show_list_selector     (GtdNewTaskRow      *self,
+                                                                  gboolean            show_list_selector);
+
 G_END_DECLS
 
 #endif /* GTD_NEW_TASK_ROW_H */
diff --git a/src/gtd-task-list-view.c b/src/gtd-task-list-view.c
index dae4a8a..94e39ed 100644
--- a/src/gtd-task-list-view.c
+++ b/src/gtd-task-list-view.c
@@ -1142,15 +1142,18 @@ gtd_task_list_view__task_added (GtdTaskList     *list,
 }
 
 static void
-gtd_task_list_view__create_task (GtdTaskRow *row,
-                                 GtdTask    *task,
-                                 gpointer    user_data)
+gtd_task_list_view__create_task (GtdTaskRow  *row,
+                                 GtdTask     *task,
+                                 GtdTaskList *list,
+                                 gpointer     user_data)
 {
   GtdTaskListViewPrivate *priv;
-  GtdTaskList *list;
 
   priv = GTD_TASK_LIST_VIEW (user_data)->priv;
-  list = priv->task_list;
+
+  /* If there's a task list set, always go for it */
+  if (priv->task_list)
+    list = priv->task_list;
 
   /*
    * If there is no current list set, use the default list from the
@@ -1835,6 +1838,8 @@ gtd_task_list_view_set_task_list (GtdTaskListView *view,
   if (priv->task_list == list)
     return;
 
+  gtd_new_task_row_set_show_list_selector (GTD_NEW_TASK_ROW (priv->new_task_row), list == NULL);
+
   /*
    * Disconnect the old GtdTaskList signals.
    */


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