[gnome-todo] task-list-view: Reintroduce empty state
- From: Georges Basile Stavracas Neto <gbsneto src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-todo] task-list-view: Reintroduce empty state
- Date: Tue, 13 Apr 2021 15:00:27 +0000 (UTC)
commit 45709723cb9d5ae3dbf9ac690933e63764036b3d
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date: Tue Apr 13 11:51:56 2021 -0300
task-list-view: Reintroduce empty state
Now redesigned with Jakub's new artwork. GtdTaskListView requires a new
filter list to figure if the empty state should be about "no more tasks
remaining" or "no tasks whatsoever" cases.
src/gui/gtd-task-list-view.c | 104 ++++++++++++++++++++++++++++-
src/gui/gtd-task-list-view.ui | 148 ++++++++++++++++++++++++++----------------
2 files changed, 193 insertions(+), 59 deletions(-)
---
diff --git a/src/gui/gtd-task-list-view.c b/src/gui/gtd-task-list-view.c
index bf89e022..19f7da6f 100644
--- a/src/gui/gtd-task-list-view.c
+++ b/src/gui/gtd-task-list-view.c
@@ -69,7 +69,9 @@
typedef struct
{
+ GtdEmptyListWidget *empty_list_widget;
GtkListBox *listbox;
+ GtkStack *main_stack;
GtkListBoxRow *new_task_row;
GtkWidget *scrolled_window;
GtkStack *stack;
@@ -82,6 +84,9 @@ typedef struct
GListModel *model;
GDateTime *default_date;
+ GListModel *incomplete_tasks_model;
+ guint n_incomplete_tasks;
+
guint scroll_to_bottom_handler_id;
GHashTable *task_to_row;
@@ -142,10 +147,19 @@ typedef struct
#define TASK_REMOVED_NOTIFICATION_ID "task-removed-id"
+static gboolean filter_complete_func (gpointer item,
+ gpointer user_data);
+
static void on_clear_completed_tasks_activated_cb (GSimpleAction *simple,
GVariant *parameter,
gpointer user_data);
+static void on_incomplete_tasks_items_changed_cb (GListModel *model,
+ guint position,
+ guint n_removed,
+ guint n_added,
+ GtdTaskListView *self);
+
static void on_remove_task_row_cb (GtdTaskRow *row,
GtdTaskListView *self);
@@ -276,11 +290,88 @@ schedule_scroll_to_bottom (GtdTaskListView *self)
priv->scroll_to_bottom_handler_id = g_timeout_add (250, scroll_to_bottom_cb, self);
}
+static void
+update_empty_state (GtdTaskListView *self)
+{
+ GtdTaskListViewPrivate *priv = gtd_task_list_view_get_instance_private (self);
+ gboolean show_empty_list_widget;
+ gboolean is_empty;
+
+ g_assert (GTD_IS_TASK_LIST_VIEW (self));
+
+ is_empty = g_list_model_get_n_items (priv->model) == 0;
+ gtd_empty_list_widget_set_is_empty (priv->empty_list_widget, is_empty);
+
+ show_empty_list_widget = !GTD_IS_TASK_LIST (priv->model) &&
+ (is_empty || priv->n_incomplete_tasks == 0);
+ gtk_stack_set_visible_child_name (priv->main_stack,
+ show_empty_list_widget ? "empty-list" : "task-list");
+}
+
+static void
+update_incomplete_tasks_model (GtdTaskListView *self)
+{
+ GtdTaskListViewPrivate *priv = gtd_task_list_view_get_instance_private (self);
+
+ if (!priv->incomplete_tasks_model)
+ {
+ g_autoptr (GtkFilterListModel) filter_model = NULL;
+ GtkCustomFilter *filter;
+
+ filter = gtk_custom_filter_new (filter_complete_func, self, NULL);
+ filter_model = gtk_filter_list_model_new (NULL, GTK_FILTER (filter));
+ gtk_filter_list_model_set_incremental (filter_model, TRUE);
+
+ priv->incomplete_tasks_model = G_LIST_MODEL (g_steal_pointer (&filter_model));
+ }
+
+ gtk_filter_list_model_set_model (GTK_FILTER_LIST_MODEL (priv->incomplete_tasks_model),
+ priv->model);
+ priv->n_incomplete_tasks = g_list_model_get_n_items (priv->incomplete_tasks_model);
+
+ g_signal_connect (priv->incomplete_tasks_model,
+ "items-changed",
+ G_CALLBACK (on_incomplete_tasks_items_changed_cb),
+ self);
+}
+
/*
* Callbacks
*/
+static gboolean
+filter_complete_func (gpointer item,
+ gpointer user_data)
+{
+ GtdTask *task = (GtdTask*) item;
+ return !gtd_task_get_complete (task);
+}
+
+static void
+on_incomplete_tasks_items_changed_cb (GListModel *model,
+ guint position,
+ guint n_removed,
+ guint n_added,
+ GtdTaskListView *self)
+{
+ GtdTaskListViewPrivate *priv = gtd_task_list_view_get_instance_private (self);
+
+ priv->n_incomplete_tasks -= n_removed;
+ priv->n_incomplete_tasks += n_added;
+
+ update_empty_state (self);
+}
+
+static void
+on_empty_list_widget_add_tasks_cb (GtdEmptyListWidget *empty_list_widget,
+ GtdTaskListView *self)
+{
+ GtdTaskListViewPrivate *priv = gtd_task_list_view_get_instance_private (self);
+
+ gtk_stack_set_visible_child_name (priv->main_stack, "task-list");
+}
+
static GtkWidget*
create_row_for_task_cb (gpointer item,
gpointer user_data)
@@ -944,6 +1035,7 @@ gtd_task_list_view_finalize (GObject *object)
g_clear_handle_id (&priv->scroll_to_bottom_handler_id, g_source_remove);
g_clear_pointer (&priv->task_to_row, g_hash_table_destroy);
g_clear_pointer (&priv->default_date, g_date_time_unref);
+ g_clear_object (&priv->incomplete_tasks_model);
g_clear_object (&priv->renderer);
g_clear_object (&priv->model);
@@ -1051,12 +1143,15 @@ gtd_task_list_view_constructed (GObject *object)
static void
gtd_task_list_view_map (GtkWidget *widget)
{
- GtdTaskListViewPrivate *priv;
+ GtdTaskListView *self = GTD_TASK_LIST_VIEW (widget);
+ GtdTaskListViewPrivate *priv = gtd_task_list_view_get_instance_private (self);
GtkRoot *root;
+
+ update_empty_state (self);
+
GTK_WIDGET_CLASS (gtd_task_list_view_parent_class)->map (widget);
- priv = GTD_TASK_LIST_VIEW (widget)->priv;
root = gtk_widget_get_root (widget);
/* Clear previously added "list" actions */
@@ -1159,12 +1254,15 @@ gtd_task_list_view_class_init (GtdTaskListViewClass *klass)
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/todo/ui/gtd-task-list-view.ui");
gtk_widget_class_bind_template_child_private (widget_class, GtdTaskListView, due_date_sizegroup);
+ gtk_widget_class_bind_template_child_private (widget_class, GtdTaskListView, empty_list_widget);
gtk_widget_class_bind_template_child_private (widget_class, GtdTaskListView, listbox);
+ gtk_widget_class_bind_template_child_private (widget_class, GtdTaskListView, main_stack);
gtk_widget_class_bind_template_child_private (widget_class, GtdTaskListView, new_task_row);
gtk_widget_class_bind_template_child_private (widget_class, GtdTaskListView, tasklist_name_sizegroup);
gtk_widget_class_bind_template_child_private (widget_class, GtdTaskListView, scrolled_window);
gtk_widget_class_bind_template_child_private (widget_class, GtdTaskListView, stack);
+ gtk_widget_class_bind_template_callback (widget_class, on_empty_list_widget_add_tasks_cb);
gtk_widget_class_bind_template_callback (widget_class, on_listbox_row_activated_cb);
gtk_widget_class_bind_template_callback (widget_class, on_new_task_row_entered_cb);
gtk_widget_class_bind_template_callback (widget_class, on_new_task_row_exited_cb);
@@ -1300,6 +1398,8 @@ gtd_task_list_view_set_model (GtdTaskListView *view,
NULL);
schedule_scroll_to_bottom (view);
+ update_incomplete_tasks_model (view);
+ update_empty_state (view);
if (priv->task_list_selector_behavior == GTD_TASK_LIST_SELECTOR_BEHAVIOR_AUTOMATIC)
gtd_new_task_row_set_show_list_selector (GTD_NEW_TASK_ROW (priv->new_task_row), !GTD_IS_TASK_LIST
(model));
diff --git a/src/gui/gtd-task-list-view.ui b/src/gui/gtd-task-list-view.ui
index 9a0775dc..1682bf95 100644
--- a/src/gui/gtd-task-list-view.ui
+++ b/src/gui/gtd-task-list-view.ui
@@ -3,74 +3,108 @@
<template class="GtdTaskListView" parent="GtkBox">
<property name="vexpand">1</property>
<property name="orientation">vertical</property>
+
+ <!-- Main stack -->
<child>
- <object class="GtkScrolledWindow" id="scrolled_window">
- <property name="can_focus">1</property>
- <property name="hexpand">1</property>
- <property name="vexpand">1</property>
- <property name="min-content-height">320</property>
- <property name="hscrollbar-policy">never</property>
+ <object class="GtkStack" id="main_stack">
+ <property name="hexpand">true</property>
+ <property name="vexpand">true</property>
+ <property name="transition-type">crossfade</property>
+
+ <!-- Task list page -->
<child>
- <object class="GtkStack" id="stack">
- <property name="hexpand">true</property>
- <property name="vexpand">true</property>
- <property name="transition-type">crossfade</property>
- <child>
- <object class="GtkStackPage">
- <property name="name">listbox</property>
- <property name="child">
- <object class="GtdWidget">
- <property name="hexpand">1</property>
- <property name="vexpand">1</property>
- <property name="halign">center</property>
- <property name="layout-manager">
- <object class="GtdMaxSizeLayout">
- <property name="max-width">700</property>
- </object>
- </property>
+ <object class="GtkStackPage">
+ <property name="name">task-list</property>
+ <property name="child">
+ <object class="GtkScrolledWindow" id="scrolled_window">
+ <property name="can_focus">1</property>
+ <property name="hexpand">1</property>
+ <property name="vexpand">1</property>
+ <property name="min-content-height">320</property>
+ <property name="hscrollbar-policy">never</property>
+ <child>
+ <object class="GtkStack" id="stack">
+ <property name="hexpand">true</property>
+ <property name="vexpand">true</property>
+ <property name="transition-type">crossfade</property>
<child>
- <object class="GtkBox">
- <property name="margin-top">6</property>
- <property name="margin-bottom">64</property>
- <property name="margin-start">18</property>
- <property name="margin-end">18</property>
- <property name="orientation">vertical</property>
- <child>
- <object class="GtkListBox" id="listbox">
+ <object class="GtkStackPage">
+ <property name="name">listbox</property>
+ <property name="child">
+ <object class="GtdWidget">
<property name="hexpand">1</property>
- <property name="selection_mode">none</property>
- <signal name="row-activated" handler="on_listbox_row_activated_cb"
object="GtdTaskListView" swapped="no"/>
- <style>
- <class name="transparent"/>
- </style>
+ <property name="vexpand">1</property>
+ <property name="halign">center</property>
+ <property name="layout-manager">
+ <object class="GtdMaxSizeLayout">
+ <property name="max-width">700</property>
+ </object>
+ </property>
+ <child>
+ <object class="GtkBox">
+ <property name="margin-top">6</property>
+ <property name="margin-bottom">64</property>
+ <property name="margin-start">18</property>
+ <property name="margin-end">18</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkListBox" id="listbox">
+ <property name="hexpand">1</property>
+ <property name="selection_mode">none</property>
+ <signal name="row-activated" handler="on_listbox_row_activated_cb"
object="GtdTaskListView" swapped="no"/>
+ <style>
+ <class name="transparent"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtdNewTaskRow" id="new_task_row">
+ <property name="margin-bottom">24</property>
+ <signal name="enter" handler="on_new_task_row_entered_cb"
object="GtdTaskListView" swapped="yes"/>
+ <signal name="exit" handler="on_new_task_row_exited_cb"
object="GtdTaskListView" swapped="yes"/>
+ </object>
+ </child>
+ </object>
+ </child>
</object>
- </child>
- <child>
- <object class="GtdNewTaskRow" id="new_task_row">
- <property name="margin-bottom">24</property>
- <signal name="enter" handler="on_new_task_row_entered_cb"
object="GtdTaskListView" swapped="yes"/>
- <signal name="exit" handler="on_new_task_row_exited_cb" object="GtdTaskListView"
swapped="yes"/>
+
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkStackPage">
+ <property name="name">loading</property>
+ <property name="child">
+ <object class="GtkSpinner">
+ <property name="spinning">true</property>
+ <property name="width-request">96</property>
+ <property name="height-request">96</property>
</object>
- </child>
+ </property>
</object>
</child>
</object>
-
- </property>
+ </child>
</object>
- </child>
- <child>
- <object class="GtkStackPage">
- <property name="name">loading</property>
- <property name="child">
- <object class="GtkSpinner">
- <property name="spinning">true</property>
- <property name="width-request">96</property>
- <property name="height-request">96</property>
- </object>
- </property>
+
+ </property>
+ </object>
+ </child>
+
+ <!-- Empty list widget -->
+ <child>
+ <object class="GtkStackPage">
+ <property name="name">empty-list</property>
+ <property name="child">
+ <object class="GtdEmptyListWidget" id="empty_list_widget">
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <signal name="add-tasks" handler="on_empty_list_widget_add_tasks_cb"
object="GtdTaskListView" swapped="no" />
+ <layout>
+ <property name="measure">true</property>
+ </layout>
</object>
- </child>
+ </property>
</object>
</child>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]