diff options
author | Georges Basile Stavracas Neto <georges.stavracas@gmail.com> | 2020-09-27 14:43:04 -0300 |
---|---|---|
committer | Georges Basile Stavracas Neto <georges.stavracas@gmail.com> | 2020-09-27 14:43:04 -0300 |
commit | e6b4270fcfa00b2947cfff61cba8137131bb722c (patch) | |
tree | 77edfa94c19ee1129f9cff9c556b1467792f390c | |
parent | 75f289de59ed9897f40c4e23647e80c6e037ca21 (diff) | |
download | gnome-todo-gbsneto/listview2.tar.gz |
Initial port to GtkListViewgbsneto/listview2
Various APIs needed to be revisited or simply removed to support this.
-rw-r--r-- | src/gui/gtd-edit-pane.c | 10 | ||||
-rw-r--r-- | src/gui/gtd-new-task-row.ui | 11 | ||||
-rw-r--r-- | src/gui/gtd-task-list-popover.c | 4 | ||||
-rw-r--r-- | src/gui/gtd-task-list-view-model.c | 210 | ||||
-rw-r--r-- | src/gui/gtd-task-list-view-model.h | 43 | ||||
-rw-r--r-- | src/gui/gtd-task-list-view.c | 885 | ||||
-rw-r--r-- | src/gui/gtd-task-list-view.h | 22 | ||||
-rw-r--r-- | src/gui/gtd-task-list-view.ui | 102 | ||||
-rw-r--r-- | src/gui/gtd-task-row.c | 91 | ||||
-rw-r--r-- | src/gui/gtd-task-row.h | 10 | ||||
-rw-r--r-- | src/gui/gtd-task-row.ui | 14 | ||||
-rw-r--r-- | src/meson.build | 1 | ||||
-rw-r--r-- | src/plugins/inbox-panel/gtd-inbox-panel.c | 2 |
13 files changed, 483 insertions, 922 deletions
diff --git a/src/gui/gtd-edit-pane.c b/src/gui/gtd-edit-pane.c index 9f9dabc5..07612fca 100644 --- a/src/gui/gtd-edit-pane.c +++ b/src/gui/gtd-edit-pane.c @@ -74,25 +74,25 @@ static void update_date_widgets (GtdEditPane *self) { g_autoptr (GDateTime) dt = NULL; - gchar *text; + g_autofree gchar *text = NULL; g_return_if_fail (GTD_IS_EDIT_PANE (self)); - dt = self->task ? g_date_time_ref (gtd_task_get_due_date (self->task)) : NULL; - text = dt ? g_date_time_format (dt, "%x") : NULL; + dt = self->task ? gtd_task_get_due_date (self->task) : NULL; g_signal_handlers_block_by_func (self->calendar, on_date_selected_cb, self); if (!dt) dt = g_date_time_new_now_local (); + else + g_date_time_ref (dt); gtk_calendar_select_day (self->calendar, dt); g_signal_handlers_unblock_by_func (self->calendar, on_date_selected_cb, self); + text = dt ? g_date_time_format (dt, "%x") : NULL; gtk_label_set_label (self->date_label, text ? text : _("No date set")); - - g_free (text); } diff --git a/src/gui/gtd-new-task-row.ui b/src/gui/gtd-new-task-row.ui index b28a1267..12a0df01 100644 --- a/src/gui/gtd-new-task-row.ui +++ b/src/gui/gtd-new-task-row.ui @@ -3,9 +3,18 @@ <requires lib="gtk+" version="3.16"/> <template class="GtdNewTaskRow" parent="GtkWidget"> <property name="can_focus">1</property> - <property name="margin-top">12</property> + <property name="margin-start">12</property> + <property name="margin-end">12</property> + <property name="margin-top">6</property> + <property name="margin-bottom">6</property> <property name="height-request">42</property> <property name="css-name">newtaskrow</property> + <property name="halign">center</property> + <property name="layout-manager"> + <object class="GtdMaxSizeLayout"> + <property name="max-width">700</property> + </object> + </property> <child> <object class="GtkEntry" id="entry"> <property name="can_focus">1</property> diff --git a/src/gui/gtd-task-list-popover.c b/src/gui/gtd-task-list-popover.c index 176c0e5b..a81d4618 100644 --- a/src/gui/gtd-task-list-popover.c +++ b/src/gui/gtd-task-list-popover.c @@ -243,12 +243,12 @@ gtd_task_list_popover_init (GtdTaskListPopover *self) GtdManager *manager = gtd_manager_get_default (); GtkCustomFilter *filter; + gtk_widget_init_template (GTK_WIDGET (self)); + filter = gtk_custom_filter_new (filter_listbox_cb, self, NULL); self->filter_model = gtk_filter_list_model_new (gtd_manager_get_task_lists_model (manager), GTK_FILTER (filter)); - gtk_widget_init_template (GTK_WIDGET (self)); - gtk_list_box_bind_model (self->listbox, G_LIST_MODEL (self->filter_model), create_list_row_cb, diff --git a/src/gui/gtd-task-list-view-model.c b/src/gui/gtd-task-list-view-model.c new file mode 100644 index 00000000..87b6f41d --- /dev/null +++ b/src/gui/gtd-task-list-view-model.c @@ -0,0 +1,210 @@ +/* gtd-task-list-view-model.c + * + * Copyright 2020 Georges Basile Stavracas Neto <georges.stavracas@gmail.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "gtd-task-list-view-model.h" + + +/* + * Sentinel + */ + +struct _GtdSentinel +{ + GObject parent_instance; +}; + +G_DEFINE_TYPE (GtdSentinel, gtd_sentinel, G_TYPE_OBJECT); + +static void +gtd_sentinel_init (GtdSentinel *self) +{ +} + +static void +gtd_sentinel_class_init (GtdSentinelClass *klass) +{ +} + + + +struct _GtdTaskListViewModel +{ + GObject parent_instance; + + GtdSentinel *sentinel; + + GListModel *model; + guint n_items; +}; + +static void g_list_model_iface_init (GListModelInterface *iface); + +G_DEFINE_TYPE_WITH_CODE (GtdTaskListViewModel, gtd_task_list_view_model, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, g_list_model_iface_init)) + + +/* + * Auxiliary methods + */ + +static void +update_n_items (GtdTaskListViewModel *self, + guint position, + guint removed, + guint added) +{ + self->n_items = self->n_items - removed + added; + g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added); +} + + +/* + * Callbacks + */ + +static void +on_model_items_changed_cb (GListModel *model, + guint position, + guint removed, + guint added, + GtdTaskListViewModel *self) +{ + update_n_items (self, position, removed, added); +} + + +/* + * GListModel interface + */ + +static guint +gtd_task_list_view_model_get_n_items (GListModel *model) +{ + GtdTaskListViewModel *self = (GtdTaskListViewModel *)model; + + return self->n_items + 1; +} + +static GType +gtd_task_list_view_model_get_item_type (GListModel *model) +{ + return G_TYPE_OBJECT; +} + +static gpointer +gtd_task_list_view_model_get_item (GListModel *model, + guint position) +{ + GtdTaskListViewModel *self = (GtdTaskListViewModel *)model; + + if (gtd_task_list_view_model_is_sentinel (self, position)) + return g_object_ref (self->sentinel); + + return g_list_model_get_item (self->model, position); +} + +static void +g_list_model_iface_init (GListModelInterface *iface) +{ + iface->get_n_items = gtd_task_list_view_model_get_n_items; + iface->get_item_type = gtd_task_list_view_model_get_item_type; + iface->get_item = gtd_task_list_view_model_get_item; +} + + +/* + * GObject overrides + */ + +static void +gtd_task_list_view_model_finalize (GObject *object) +{ + GtdTaskListViewModel *self = (GtdTaskListViewModel *)object; + + g_clear_object (&self->model); + g_clear_object (&self->sentinel); + + G_OBJECT_CLASS (gtd_task_list_view_model_parent_class)->finalize (object); +} + +static void +gtd_task_list_view_model_class_init (GtdTaskListViewModelClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gtd_task_list_view_model_finalize; +} + +static void +gtd_task_list_view_model_init (GtdTaskListViewModel *self) +{ + self->sentinel = g_object_new (GTD_TYPE_SENTINEL, NULL); +} + +GtdTaskListViewModel * +gtd_task_list_view_model_new (void) +{ + return g_object_new (GTD_TYPE_TASK_LIST_VIEW_MODEL, NULL); +} + +GListModel * +gtd_task_list_view_model_get_model (GtdTaskListViewModel *self) +{ + return self->model; +} + +void +gtd_task_list_view_model_set_model (GtdTaskListViewModel *self, + GListModel *model) +{ + guint old_n_items = self->n_items; + guint new_n_items = 0; + + if (self->model) + { + g_signal_handlers_disconnect_by_func (self->model, + on_model_items_changed_cb, + self); + } + + g_clear_object (&self->model); + + if (model) + { + self->model = g_object_ref (model); + + g_signal_connect_object (model, + "items-changed", + G_CALLBACK (on_model_items_changed_cb), + self, + 0); + + new_n_items = g_list_model_get_n_items (model); + } + + update_n_items (self, 0, old_n_items, new_n_items); +} + +gboolean +gtd_task_list_view_model_is_sentinel (GtdTaskListViewModel *self, + guint position) +{ + return position == self->n_items; +} diff --git a/src/gui/gtd-task-list-view-model.h b/src/gui/gtd-task-list-view-model.h new file mode 100644 index 00000000..7323f3ac --- /dev/null +++ b/src/gui/gtd-task-list-view-model.h @@ -0,0 +1,43 @@ +/* gtd-task-list-view-model.h + * + * Copyright 2020 Georges Basile Stavracas Neto <georges.stavracas@gmail.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include <gio/gio.h> + +G_BEGIN_DECLS + +#define GTD_TYPE_SENTINEL (gtd_sentinel_get_type ()) +G_DECLARE_FINAL_TYPE (GtdSentinel, gtd_sentinel, GTD, SENTINEL, GObject) + +#define GTD_TYPE_TASK_LIST_VIEW_MODEL (gtd_task_list_view_model_get_type()) +G_DECLARE_FINAL_TYPE (GtdTaskListViewModel, gtd_task_list_view_model, GTD, TASK_LIST_VIEW_MODEL, GObject) + +GtdTaskListViewModel* gtd_task_list_view_model_new (void); + +GListModel* gtd_task_list_view_model_get_model (GtdTaskListViewModel *self); + +void gtd_task_list_view_model_set_model (GtdTaskListViewModel *self, + GListModel *model); + +gboolean gtd_task_list_view_model_is_sentinel (GtdTaskListViewModel *self, + guint position); + +G_END_DECLS diff --git a/src/gui/gtd-task-list-view.c b/src/gui/gtd-task-list-view.c index bf89e022..d76edf5f 100644 --- a/src/gui/gtd-task-list-view.c +++ b/src/gui/gtd-task-list-view.c @@ -30,6 +30,7 @@ #include "gtd-provider.h" #include "gtd-task.h" #include "gtd-task-list.h" +#include "gtd-task-list-view-model.h" #include "gtd-task-row.h" #include "gtd-utils-private.h" #include "gtd-widget.h" @@ -58,9 +59,6 @@ * * gtd_task_list_view_set_model (view, model); * - * // Hide the '+ New task' row - * gtd_task_list_view_set_show_new_task_row (view, FALSE); - * * // Date which tasks will be automatically assigned * gtd_task_list_view_set_default_date (view, now); * ]| @@ -69,8 +67,7 @@ typedef struct { - GtkListBox *listbox; - GtkListBoxRow *new_task_row; + GtkListView *listview; GtkWidget *scrolled_window; GtkStack *stack; @@ -79,12 +76,13 @@ typedef struct gboolean show_due_date; gboolean show_list_name; gboolean handle_subtasks; - GListModel *model; GDateTime *default_date; - guint scroll_to_bottom_handler_id; + GtdTaskListViewModel *model; + gpointer active_item; + gint64 active_position; - GHashTable *task_to_row; + guint scroll_to_bottom_handler_id; /* Markup renderer*/ GtdMarkdownRenderer *renderer; @@ -94,10 +92,6 @@ typedef struct guint scroll_timeout_id; gboolean scroll_up; - /* color provider */ - GtkCssProvider *color_provider; - GdkRGBA *color; - /* action */ GActionGroup *action_group; @@ -105,10 +99,8 @@ typedef struct GtdTaskListViewHeaderFunc header_func; gpointer header_user_data; - GtdTaskRow *active_row; GtkSizeGroup *due_date_sizegroup; GtkSizeGroup *tasklist_name_sizegroup; - GtdTaskListSelectorBehavior task_list_selector_behavior; } GtdTaskListViewPrivate; struct _GtdTaskListView @@ -119,23 +111,6 @@ struct _GtdTaskListView GtdTaskListViewPrivate *priv; }; -typedef enum -{ - GTD_IDLE_STATE_STARTED, - GTD_IDLE_STATE_LOADING, - GTD_IDLE_STATE_COMPLETE, - GTD_IDLE_STATE_FINISHED, -} GtdIdleState; - -typedef struct -{ - GtdTaskListView *self; - GtdIdleState state; - GPtrArray *added; - GPtrArray *removed; - guint32 current_item; -} GtdIdleData; - #define COLOR_TEMPLATE "tasklistview {background-color: %s;}" #define DND_SCROLL_OFFSET 24 //px #define LUMINANCE(c) (0.299 * c->red + 0.587 * c->green + 0.114 * c->blue) @@ -149,12 +124,6 @@ static void on_clear_completed_tasks_activated_cb (GSimpleAction static void on_remove_task_row_cb (GtdTaskRow *row, GtdTaskListView *self); -static void on_task_row_entered_cb (GtdTaskListView *self, - GtdTaskRow *row); - -static void on_task_row_exited_cb (GtdTaskListView *self, - GtdTaskRow *row); - static gboolean scroll_to_bottom_cb (gpointer data); @@ -172,49 +141,19 @@ typedef struct enum { PROP_0, - PROP_COLOR, PROP_HANDLE_SUBTASKS, PROP_SHOW_LIST_NAME, PROP_SHOW_DUE_DATE, - PROP_SHOW_NEW_TASK_ROW, LAST_PROP }; typedef gboolean (*IterateSubtaskFunc) (GtdTaskListView *self, GtdTask *task); - /* * Auxiliary methods */ -static inline GtdTaskRow* -task_row_from_row (GtkListBoxRow *row) -{ - return GTD_TASK_ROW (gtk_list_box_row_get_child (row)); -} - -static void -set_active_row (GtdTaskListView *self, - GtdTaskRow *row) -{ - GtdTaskListViewPrivate *priv = gtd_task_list_view_get_instance_private (self); - - if (priv->active_row == row) - return; - - if (priv->active_row) - gtd_task_row_set_active (priv->active_row, FALSE); - - priv->active_row = row; - - if (row) - { - gtd_task_row_set_active (row, TRUE); - gtk_widget_grab_focus (GTK_WIDGET (row)); - } -} - static gboolean iterate_subtasks (GtdTaskListView *self, GtdTask *task, @@ -237,35 +176,6 @@ iterate_subtasks (GtdTaskListView *self, } static void -update_font_color (GtdTaskListView *self) -{ - GtdTaskListViewPrivate *priv; - GtkStyleContext *context; - GdkRGBA *color; - - priv = gtd_task_list_view_get_instance_private (self); - - if (!priv->model || !GTD_IS_TASK_LIST (priv->model)) - return; - - context = gtk_widget_get_style_context (GTK_WIDGET (self)); - color = gtd_task_list_get_color (GTD_TASK_LIST (priv->model)); - - if (LUMINANCE (color) < 0.5) - { - gtk_style_context_add_class (context, "dark"); - gtk_style_context_remove_class (context, "light"); - } - else - { - gtk_style_context_add_class (context, "light"); - gtk_style_context_remove_class (context, "dark"); - } - - gdk_rgba_free (color); -} - -static void schedule_scroll_to_bottom (GtdTaskListView *self) { GtdTaskListViewPrivate *priv = gtd_task_list_view_get_instance_private (self); @@ -276,24 +186,13 @@ schedule_scroll_to_bottom (GtdTaskListView *self) priv->scroll_to_bottom_handler_id = g_timeout_add (250, scroll_to_bottom_cb, self); } - -/* - * Callbacks - */ - static GtkWidget* -create_row_for_task_cb (gpointer item, - gpointer user_data) +create_task_row (GtdTaskListView *self) { - GtdTaskListViewPrivate *priv; - GtdTaskListView *self; - GtkWidget *listbox_row; + GtdTaskListViewPrivate *priv = gtd_task_list_view_get_instance_private (self); GtkWidget *row; - self = GTD_TASK_LIST_VIEW (user_data); - priv = gtd_task_list_view_get_instance_private (self); - - row = gtd_task_row_new (item, priv->renderer); + row = gtd_task_row_new (NULL, priv->renderer); g_object_bind_property (self, "handle-subtasks", @@ -303,21 +202,19 @@ create_row_for_task_cb (gpointer item, gtd_task_row_set_list_name_visible (GTD_TASK_ROW (row), priv->show_list_name); gtd_task_row_set_due_date_visible (GTD_TASK_ROW (row), priv->show_due_date); - - g_signal_connect_swapped (row, "enter", G_CALLBACK (on_task_row_entered_cb), self); - g_signal_connect_swapped (row, "exit", G_CALLBACK (on_task_row_exited_cb), self); + gtd_task_row_set_sizegroups (GTD_TASK_ROW (row), + priv->tasklist_name_sizegroup, + priv->due_date_sizegroup); g_signal_connect (row, "remove-task", G_CALLBACK (on_remove_task_row_cb), self); - listbox_row = gtk_list_box_row_new (); - gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (listbox_row), row); - - g_object_bind_property (row, "visible", listbox_row, "visible", G_BINDING_BIDIRECTIONAL); + return row; +} - g_hash_table_insert (priv->task_to_row, item, row); - return listbox_row; -} +/* + * Callbacks + */ static gboolean scroll_to_bottom_cb (gpointer data) @@ -346,7 +243,6 @@ scroll_to_bottom_cb (gpointer data) { gboolean ignored; - gtk_widget_grab_focus (GTK_WIDGET (priv->new_task_row)); g_signal_emit_by_name (priv->scrolled_window, "scroll-child", GTK_SCROLL_END, FALSE, &ignored); } @@ -388,7 +284,7 @@ on_clear_completed_tasks_activated_cb (GSimpleAction *simple, guint i; self = GTD_TASK_LIST_VIEW (user_data); - model = self->priv->model; + model = gtd_task_list_view_model_get_model (self->priv->model); for (i = 0; i < g_list_model_get_n_items (model); i++) { @@ -483,86 +379,36 @@ on_remove_task_row_cb (GtdTaskRow *row, /* Clear the active row */ - set_active_row (self, NULL); + gtd_task_row_set_active (row, FALSE); } static void -on_task_list_color_changed_cb (GtdTaskListView *self) -{ - GtdTaskListViewPrivate *priv = GTD_TASK_LIST_VIEW (self)->priv; - gchar *color_str; - gchar *parsed_css; - - /* Add the color to provider */ - if (priv->color) - { - color_str = gdk_rgba_to_string (priv->color); - } - else - { - GdkRGBA *color; - - color = gtd_task_list_get_color (GTD_TASK_LIST (priv->model)); - color_str = gdk_rgba_to_string (color); - - gdk_rgba_free (color); - } - - parsed_css = g_strdup_printf (COLOR_TEMPLATE, color_str); - - gtk_css_provider_load_from_data (priv->color_provider, parsed_css, -1); - - update_font_color (self); - - g_free (color_str); -} -static void -on_new_task_row_entered_cb (GtdTaskListView *self, - GtdNewTaskRow *row) -{ - set_active_row (self, NULL); -} - -static void -on_new_task_row_exited_cb (GtdTaskListView *self, - GtdNewTaskRow *row) +on_listview_activate_cb (GtkListBox *listbox, + guint position, + GtdTaskListView *self) { + GtdTaskListViewPrivate *priv = gtd_task_list_view_get_instance_private (self); + gpointer item; + gint64 old_active_position; -} + GTD_ENTRY; -static void -on_task_row_entered_cb (GtdTaskListView *self, - GtdTaskRow *row) -{ - set_active_row (self, row); -} + if (priv->active_position == position) + GTD_RETURN (); -static void -on_task_row_exited_cb (GtdTaskListView *self, - GtdTaskRow *row) -{ - GtdTaskListViewPrivate *priv = self->priv; + item = g_list_model_get_item (G_LIST_MODEL (priv->model), position); - if (row == priv->active_row) - set_active_row (self, NULL); -} + old_active_position = priv->active_position; -static void -on_listbox_row_activated_cb (GtkListBox *listbox, - GtkListBoxRow *row, - GtdTaskListView *self) -{ - GtdTaskRow *task_row; + priv->active_item = item; + priv->active_position = position; - GTD_ENTRY; + GTD_TRACE_MSG ("Activating %s at %u", G_OBJECT_TYPE_NAME (item), position); - task_row = task_row_from_row (row); + if (old_active_position != -1) + g_list_model_items_changed (G_LIST_MODEL (priv->model), old_active_position, 1, 1); - /* Toggle the row */ - if (gtd_task_row_get_active (task_row)) - set_active_row (self, NULL); - else - set_active_row (self, task_row); + g_clear_object (&item); GTD_EXIT; } @@ -573,362 +419,76 @@ on_listbox_row_activated_cb (GtkListBox *listbox, */ static void -internal_header_func (GtkListBoxRow *row, - GtkListBoxRow *before, - GtdTaskListView *view) -{ - GtkWidget *header; - GtdTask *row_task; - GtdTask *before_task; - - if (!view->priv->header_func) - return; - - row_task = before_task = NULL; - - if (row) - row_task = gtd_task_row_get_task (task_row_from_row (row)); - - if (before) - before_task = gtd_task_row_get_task (task_row_from_row (before)); - - header = view->priv->header_func (row_task, before_task, view->priv->header_user_data); - - if (header) - { - GtkWidget *real_header = gtd_widget_new (); - gtk_widget_insert_before (header, real_header, NULL); - - header = real_header; - } - - gtk_list_box_row_set_header (row, header); -} - - -/* - * Drag n' Drop functions - */ - -static gboolean -row_is_subtask_of (GtdTaskRow *row_a, - GtdTaskRow *row_b) +on_listview_setup_cb (GtkSignalListItemFactory *factory, + GtkListItem *list_item, + GtdTaskListView *self) { - GtdTask *task_a; - GtdTask *task_b; + GtkWidget *row; - task_a = gtd_task_row_get_task (row_a); - task_b = gtd_task_row_get_task (row_b); + /* Use a task row here, even if it's the sentinel */ + row = create_task_row (self); - return gtd_task_is_subtask (task_a, task_b); + gtk_list_item_set_child (list_item, row); } -static GtkListBoxRow* -get_drop_row_at_y (GtdTaskListView *self, - gdouble y) +static void +on_listview_bind_cb (GtkSignalListItemFactory *factory, + GtkListItem *list_item, + GtdTaskListView *self) { - GtdTaskListViewPrivate *priv; - GtkListBoxRow *hovered_row; - GtkListBoxRow *task_row; - GtkListBoxRow *drop_row; - gdouble row_y, row_height; - - priv = gtd_task_list_view_get_instance_private (self); - - hovered_row = gtk_list_box_get_row_at_y (priv->listbox, y); - - /* Small optimization when hovering the first row */ - if (gtk_list_box_row_get_index (hovered_row) == 0) - return hovered_row; + GtdTaskListViewPrivate *priv = gtd_task_list_view_get_instance_private (self); + GtkWidget *row; + gpointer item; - drop_row = NULL; - task_row = hovered_row; - row_height = gtk_widget_get_allocated_height (GTK_WIDGET (hovered_row)); - gtk_widget_translate_coordinates (GTK_WIDGET (priv->listbox), - GTK_WIDGET (hovered_row), - 0, - y, - NULL, - &row_y); + item = gtk_list_item_get_item (list_item); + row = gtk_list_item_get_child (list_item); - /* - * If the pointer if in the top part of the row, move the DnD row to - * the previous row. - */ - if (row_y < row_height / 2) + if (GTD_IS_TASK (item)) { - gint row_index, i; + GtdTaskRow *task_row; - row_index = gtk_list_box_row_get_index (hovered_row); - - /* Search for a valid task row */ - for (i = row_index - 1; i >= 0; i--) + if (!GTD_IS_TASK_ROW (row)) { - GtkListBoxRow *aux; - - aux = gtk_list_box_get_row_at_index (GTK_LIST_BOX (priv->listbox), i); - - /* Skip DnD, New task and hidden rows */ - if (aux && !gtk_widget_get_visible (GTK_WIDGET (aux))) - continue; - - drop_row = aux; - break; + row = create_task_row (self); + gtk_list_item_set_child (list_item, GTK_WIDGET (row)); } - } - else - { - drop_row = task_row; - } - - return drop_row ? drop_row : NULL; -} - -static void -unset_previously_highlighted_row (GtdTaskListView *self) -{ - GtdTaskListViewPrivate *priv = gtd_task_list_view_get_instance_private (self); - if (priv->highlighted_row) - { - gtd_task_row_unset_drag_offset (task_row_from_row (priv->highlighted_row)); - priv->highlighted_row = NULL; - } -} -static inline gboolean -scroll_to_dnd (gpointer user_data) -{ - GtdTaskListViewPrivate *priv; - GtkAdjustment *vadjustment; - gint value; + task_row = GTD_TASK_ROW (row); - priv = gtd_task_list_view_get_instance_private (user_data); - vadjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (priv->scrolled_window)); - value = gtk_adjustment_get_value (vadjustment) + (priv->scroll_up ? -6 : 6); - - gtk_adjustment_set_value (vadjustment, - CLAMP (value, 0, gtk_adjustment_get_upper (vadjustment))); - - return G_SOURCE_CONTINUE; -} + gtd_task_row_set_task (task_row, GTD_TASK (item)); -static void -check_dnd_scroll (GtdTaskListView *self, - gboolean should_cancel, - gdouble y) -{ - GtdTaskListViewPrivate *priv = gtd_task_list_view_get_instance_private (self); - gdouble current_y, height; + if (gtk_list_item_get_item (list_item) == priv->active_item) + { + gboolean active; - if (should_cancel) - { - if (priv->scroll_timeout_id > 0) + /* Toggle the active state if this is the active item */ + active = gtd_task_row_get_active (task_row); + g_message ("Row active: %d", active); + gtd_task_row_set_active (task_row, !active); + } + else { - g_source_remove (priv->scroll_timeout_id); - priv->scroll_timeout_id = 0; + gtd_task_row_set_active (task_row, FALSE); } - return; } - - height = gtk_widget_get_allocated_height (priv->scrolled_window); - gtk_widget_translate_coordinates (GTK_WIDGET (priv->listbox), - priv->scrolled_window, - 0, y, - NULL, ¤t_y); - - if (current_y < DND_SCROLL_OFFSET || current_y > height - DND_SCROLL_OFFSET) - { - if (priv->scroll_timeout_id > 0) - return; - - /* Start the autoscroll */ - priv->scroll_up = current_y < DND_SCROLL_OFFSET; - priv->scroll_timeout_id = g_timeout_add (25, - scroll_to_dnd, - self); - } - else + else if (GTD_IS_SENTINEL (item)) { - if (priv->scroll_timeout_id == 0) - return; - - /* Cancel the autoscroll */ - g_source_remove (priv->scroll_timeout_id); - priv->scroll_timeout_id = 0; - } -} - -static void -on_drop_target_drag_leave_cb (GtkDropTarget *drop_target, - GtdTaskListView *self) -{ - unset_previously_highlighted_row (self); - check_dnd_scroll (self, TRUE, -1); -} - -static GdkDragAction -on_drop_target_drag_motion_cb (GtkDropTarget *drop_target, - gdouble x, - gdouble y, - GtdTaskListView *self) -{ - GtdTaskListViewPrivate *priv; - GtkListBoxRow *highlighted_row; - GtdTaskRow *highlighted_task_row; - GtdTaskRow *source_task_row; - const GValue *value; - GdkDrop *drop; - GtdTask *task; - GdkDrag *drag; - gdouble x_offset; - - GTD_ENTRY; - - priv = gtd_task_list_view_get_instance_private (self); - drop = gtk_drop_target_get_drop (drop_target); - drag = gdk_drop_get_drag (drop); - - if (!drag) - { - g_info ("Only dragging task rows is supported"); - GTD_GOTO (fail); - } - - value = gtk_drop_target_get_value (drop_target); - task = g_value_get_object (value); - - source_task_row = g_hash_table_lookup (priv->task_to_row, task); - - /* Update the x value according to the current offset */ - if (gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL) - x += gtd_task_row_get_x_offset (source_task_row); - else - x -= gtd_task_row_get_x_offset (source_task_row); + GListModel *model = gtd_task_list_view_model_get_model (priv->model); - unset_previously_highlighted_row (self); - - highlighted_row = get_drop_row_at_y (self, y); - if (!highlighted_row) - GTD_GOTO (success); - - highlighted_task_row = task_row_from_row (highlighted_row); - - /* Forbid dropping a row over a subtask row */ - if (row_is_subtask_of (source_task_row, highlighted_task_row)) - GTD_GOTO (fail); - - gtk_widget_translate_coordinates (GTK_WIDGET (priv->listbox), - GTK_WIDGET (highlighted_task_row), - x, - 0, - &x_offset, - NULL); - - gtd_task_row_set_drag_offset (highlighted_task_row, source_task_row, x_offset); - priv->highlighted_row = highlighted_row; - -success: - check_dnd_scroll (self, FALSE, y); - GTD_RETURN (GDK_ACTION_MOVE); - -fail: - GTD_RETURN (0); -} - -static gboolean -on_drop_target_drag_drop_cb (GtkDropTarget *drop_target, - const GValue *value, - gdouble x, - gdouble y, - GtdTaskListView *self) -{ - GtdTaskListViewPrivate *priv; - GtkListBoxRow *drop_row; - GtdProvider *provider; - GtdTaskRow *hovered_row; - GtkWidget *row; - GtdTask *new_parent_task; - GtdTask *hovered_task; - GtdTask *source_task; - GdkDrop *drop; - GdkDrag *drag; - gint64 current_position; - gint64 new_position; - - GTD_ENTRY; - - priv = gtd_task_list_view_get_instance_private (self); - drop = gtk_drop_target_get_drop (drop_target); - drag = gdk_drop_get_drag (drop); - - if (!drag) - { - g_info ("Only dragging task rows is supported"); - GTD_RETURN (FALSE); - } - - unset_previously_highlighted_row (self); - - source_task = g_value_get_object (value); - - /* - * When the drag operation began, the source row was hidden. Now is the time - * to show it again. - */ - row = g_hash_table_lookup (priv->task_to_row, source_task); - gtk_widget_show (row); - - drop_row = get_drop_row_at_y (self, y); - hovered_row = task_row_from_row (drop_row); - hovered_task = gtd_task_row_get_task (hovered_row); - new_parent_task = gtd_task_row_get_dnd_drop_task (hovered_row); - - g_assert (source_task != NULL); - g_assert (source_task != new_parent_task); - - if (new_parent_task) - { - /* Forbid adding the parent task as a subtask */ - if (gtd_task_is_subtask (source_task, new_parent_task)) + if (!GTD_IS_NEW_TASK_ROW (row)) { - gdk_drop_finish (drop, 0); - GTD_RETURN (FALSE); + row = gtd_new_task_row_new (); + gtk_list_item_set_child (list_item, GTK_WIDGET (row)); } - GTD_TRACE_MSG ("Making '%s' (%s) subtask of '%s' (%s)", - gtd_task_get_title (source_task), - gtd_object_get_uid (GTD_OBJECT (source_task)), - gtd_task_get_title (new_parent_task), - gtd_object_get_uid (GTD_OBJECT (new_parent_task))); - - gtd_task_add_subtask (new_parent_task, source_task); + gtd_new_task_row_set_show_list_selector (GTD_NEW_TASK_ROW (row), + !GTD_IS_TASK_LIST (model)); } else { - GtdTask *current_parent_task = gtd_task_get_parent (source_task); - if (current_parent_task) - gtd_task_remove_subtask (current_parent_task, source_task); + g_assert_not_reached (); } - - /* - * FIXME: via DnD, we only support moving the task to below another - * task, thus the "+ 1" - */ - new_position = gtd_task_get_position (hovered_task) + 1; - current_position = gtd_task_get_position (source_task); - - if (new_position != current_position) - gtd_task_list_move_task_to_position (GTD_TASK_LIST (priv->model), source_task, new_position); - - /* Finally, save the task */ - provider = gtd_task_list_get_provider (gtd_task_get_list (source_task)); - gtd_provider_update_task (provider, source_task, NULL, NULL, NULL); - - check_dnd_scroll (self, TRUE, -1); - gdk_drop_finish (drop, GDK_ACTION_MOVE); - - GTD_RETURN (TRUE); } @@ -942,7 +502,6 @@ gtd_task_list_view_finalize (GObject *object) GtdTaskListViewPrivate *priv = GTD_TASK_LIST_VIEW (object)->priv; 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->renderer); g_clear_object (&priv->model); @@ -960,10 +519,6 @@ gtd_task_list_view_get_property (GObject *object, switch (prop_id) { - case PROP_COLOR: - g_value_set_boxed (value, self->priv->color); - break; - case PROP_HANDLE_SUBTASKS: g_value_set_boolean (value, self->priv->handle_subtasks); break; @@ -976,10 +531,6 @@ gtd_task_list_view_get_property (GObject *object, g_value_set_boolean (value, self->priv->show_list_name); break; - case PROP_SHOW_NEW_TASK_ROW: - g_value_set_boolean (value, gtk_widget_get_visible (GTK_WIDGET (self->priv->new_task_row))); - break; - default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -995,10 +546,6 @@ gtd_task_list_view_set_property (GObject *object, switch (prop_id) { - case PROP_COLOR: - gtd_task_list_view_set_color (self, g_value_get_boxed (value)); - break; - case PROP_HANDLE_SUBTASKS: gtd_task_list_view_set_handle_subtasks (self, g_value_get_boolean (value)); break; @@ -1011,10 +558,6 @@ gtd_task_list_view_set_property (GObject *object, gtd_task_list_view_set_show_list_name (self, g_value_get_boolean (value)); break; - case PROP_SHOW_NEW_TASK_ROW: - gtd_task_list_view_set_show_new_task_row (self, g_value_get_boolean (value)); - break; - default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -1034,13 +577,6 @@ gtd_task_list_view_constructed (GObject *object) gtd_task_list_view_entries, G_N_ELEMENTS (gtd_task_list_view_entries), object); - - /* css provider */ - self->priv->color_provider = gtk_css_provider_new (); - - gtk_style_context_add_provider (gtk_widget_get_style_context (GTK_WIDGET (self)), - GTK_STYLE_PROVIDER (self->priv->color_provider), - GTK_STYLE_PROVIDER_PRIORITY_APPLICATION + 2); } @@ -1086,21 +622,6 @@ gtd_task_list_view_class_init (GtdTaskListViewClass *klass) g_type_ensure (GTD_TYPE_EMPTY_LIST_WIDGET); /** - * GtdTaskListView::color: - * - * The custom color of this list. If there is a custom color set, - * the tasklist's color is ignored. - */ - g_object_class_install_property ( - object_class, - PROP_COLOR, - g_param_spec_boxed ("color", - "Color of the task list view", - "The custom color of this task list view", - GDK_TYPE_RGBA, - G_PARAM_READWRITE)); - - /** * GtdTaskListView::handle-subtasks: * * Whether the list is able to handle subtasks. @@ -1115,20 +636,6 @@ gtd_task_list_view_class_init (GtdTaskListViewClass *klass) G_PARAM_READWRITE)); /** - * GtdTaskListView::show-new-task-row: - * - * Whether the list shows the "New Task" row or not. - */ - g_object_class_install_property ( - object_class, - PROP_SHOW_NEW_TASK_ROW, - g_param_spec_boolean ("show-new-task-row", - "Whether it shows the New Task row", - "Whether the list shows the New Task row, or not", - TRUE, - G_PARAM_READWRITE)); - - /** * GtdTaskListView::show-list-name: * * Whether the task rows should show the list name. @@ -1159,17 +666,14 @@ 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, listbox); - gtk_widget_class_bind_template_child_private (widget_class, GtdTaskListView, new_task_row); + gtk_widget_class_bind_template_child_private (widget_class, GtdTaskListView, listview); 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_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); - gtk_widget_class_bind_template_callback (widget_class, on_task_row_entered_cb); - gtk_widget_class_bind_template_callback (widget_class, on_task_row_exited_cb); + gtk_widget_class_bind_template_callback (widget_class, on_listview_activate_cb); + gtk_widget_class_bind_template_callback (widget_class, on_listview_setup_cb); + gtk_widget_class_bind_template_callback (widget_class, on_listview_bind_cb); gtk_widget_class_set_css_name (widget_class, "tasklistview"); } @@ -1178,15 +682,13 @@ static void gtd_task_list_view_init (GtdTaskListView *self) { GtdTaskListViewPrivate *priv; - GtkDropTarget *target; + GtkNoSelection *no_selection; priv = gtd_task_list_view_get_instance_private (self); self->priv = priv; - priv->task_list_selector_behavior = GTD_TASK_LIST_SELECTOR_BEHAVIOR_AUTOMATIC; - priv->task_to_row = g_hash_table_new (NULL, NULL); - + priv->active_position = -1; priv->can_toggle = TRUE; priv->handle_subtasks = TRUE; priv->show_due_date = TRUE; @@ -1194,15 +696,11 @@ gtd_task_list_view_init (GtdTaskListView *self) gtk_widget_init_template (GTK_WIDGET (self)); - target = gtk_drop_target_new (GTD_TYPE_TASK, GDK_ACTION_MOVE); - gtk_drop_target_set_preload (target, TRUE); - g_signal_connect (target, "drop", G_CALLBACK (on_drop_target_drag_drop_cb), self); - g_signal_connect (target, "leave", G_CALLBACK (on_drop_target_drag_leave_cb), self); - g_signal_connect (target, "motion", G_CALLBACK (on_drop_target_drag_motion_cb), self); - - gtk_widget_add_controller (GTK_WIDGET (priv->listbox), GTK_EVENT_CONTROLLER (target)); - priv->renderer = gtd_markdown_renderer_new (); + + priv->model = gtd_task_list_view_model_new (); + no_selection = gtk_no_selection_new (G_LIST_MODEL (priv->model)); + gtk_list_view_set_model (priv->listview, GTK_SELECTION_MODEL (no_selection)); } /** @@ -1219,38 +717,6 @@ gtd_task_list_view_new (void) } /** - * gtd_task_list_view_get_show_new_task_row: - * @view: a #GtdTaskListView - * - * Gets whether @view shows the new task row or not. - * - * Returns: %TRUE if @view is shows the new task row, %FALSE otherwise - */ -gboolean -gtd_task_list_view_get_show_new_task_row (GtdTaskListView *self) -{ - g_return_val_if_fail (GTD_IS_TASK_LIST_VIEW (self), FALSE); - - return gtk_widget_get_visible (GTK_WIDGET (self->priv->new_task_row)); -} - -/** - * gtd_task_list_view_set_show_new_task_row: - * @view: a #GtdTaskListView - * - * Sets the #GtdTaskListView:show-new-task-mode property of @view. - */ -void -gtd_task_list_view_set_show_new_task_row (GtdTaskListView *view, - gboolean show_new_task_row) -{ - g_return_if_fail (GTD_IS_TASK_LIST_VIEW (view)); - - gtk_widget_set_visible (GTK_WIDGET (view->priv->new_task_row), show_new_task_row); - g_object_notify (G_OBJECT (view), "show-new-task-row"); -} - -/** * gtd_task_list_view_get_model: * @view: a #GtdTaskListView * @@ -1262,9 +728,12 @@ gtd_task_list_view_set_show_new_task_row (GtdTaskListView *view, GListModel* gtd_task_list_view_get_model (GtdTaskListView *view) { + GtdTaskListViewPrivate *priv; + g_return_val_if_fail (GTD_IS_TASK_LIST_VIEW (view), NULL); - return view->priv->model; + priv = gtd_task_list_view_get_instance_private (view); + return gtd_task_list_view_model_get_model (priv->model); } /** @@ -1280,45 +749,17 @@ gtd_task_list_view_set_model (GtdTaskListView *view, GListModel *model) { GtdTaskListViewPrivate *priv; - g_autoptr (GdkRGBA) color = NULL; - g_autofree gchar *parsed_css = NULL; - g_autofree gchar *color_str = NULL; - GtdTaskList *list; g_return_if_fail (GTD_IS_TASK_LIST_VIEW (view)); g_return_if_fail (G_IS_LIST_MODEL (model)); priv = gtd_task_list_view_get_instance_private (view); - if (!g_set_object (&priv->model, model)) - return; - - gtk_list_box_bind_model (priv->listbox, - model, - create_row_for_task_cb, - view, - NULL); + priv->active_item = NULL; + priv->active_position = -1; + gtd_task_list_view_model_set_model (priv->model, model); schedule_scroll_to_bottom (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)); - - if (!GTD_IS_TASK_LIST (model)) - return; - - list = GTD_TASK_LIST (model); - - g_debug ("%p: Setting task list to '%s'", view, gtd_task_list_get_name (list)); - - /* Add the color to provider */ - color = gtd_task_list_get_color (list); - color_str = gdk_rgba_to_string (color); - parsed_css = g_strdup_printf (COLOR_TEMPLATE, color_str); - - //gtk_css_provider_load_from_data (priv->color_provider, parsed_css, -1); - - update_font_color (view); } /** @@ -1352,22 +793,7 @@ gtd_task_list_view_set_show_list_name (GtdTaskListView *view, if (view->priv->show_list_name != show_list_name) { - GtkWidget *child; - view->priv->show_list_name = show_list_name; - - for (child = gtk_widget_get_first_child (GTK_WIDGET (view->priv->listbox)); - child; - child = gtk_widget_get_next_sibling (child)) - { - GtkWidget *row_child = gtk_list_box_row_get_child (GTK_LIST_BOX_ROW (child)); - - if (!GTD_IS_TASK_ROW (row_child)) - continue; - - gtd_task_row_set_list_name_visible (GTD_TASK_ROW (row_child), show_list_name); - } - g_object_notify (G_OBJECT (view), "show-list-name"); } } @@ -1401,7 +827,6 @@ gtd_task_list_view_set_show_due_date (GtdTaskListView *self, gboolean show_due_date) { GtdTaskListViewPrivate *priv; - GtkWidget *child; g_return_if_fail (GTD_IS_TASK_LIST_VIEW (self)); @@ -1411,19 +836,6 @@ gtd_task_list_view_set_show_due_date (GtdTaskListView *self, return; priv->show_due_date = show_due_date; - - for (child = gtk_widget_get_first_child (GTK_WIDGET (priv->listbox)); - child; - child = gtk_widget_get_next_sibling (child)) - { - GtkWidget *row_child = gtk_list_box_row_get_child (GTK_LIST_BOX_ROW (child)); - - if (!GTD_IS_TASK_ROW (row_child)) - continue; - - gtd_task_row_set_due_date_visible (GTD_TASK_ROW (row_child), show_due_date); - } - g_object_notify (G_OBJECT (self), "show-due-date"); } @@ -1453,21 +865,11 @@ gtd_task_list_view_set_header_func (GtdTaskListView *view, { priv->header_func = func; priv->header_user_data = user_data; - - gtk_list_box_set_header_func (priv->listbox, - (GtkListBoxUpdateHeaderFunc) internal_header_func, - view, - NULL); } else { priv->header_func = NULL; priv->header_user_data = NULL; - - gtk_list_box_set_header_func (priv->listbox, - NULL, - NULL, - NULL); } } @@ -1516,57 +918,6 @@ gtd_task_list_view_set_default_date (GtdTaskListView *self, } /** - * gtd_task_list_view_get_color: - * @self: a #GtdTaskListView - * - * Retrieves the custom color of @self. - * - * Returns: (nullable): a #GdkRGBA, or %NULL if none is set. - */ -GdkRGBA* -gtd_task_list_view_get_color (GtdTaskListView *self) -{ - GtdTaskListViewPrivate *priv; - - g_return_val_if_fail (GTD_IS_TASK_LIST_VIEW (self), NULL); - - priv = gtd_task_list_view_get_instance_private (self); - - return priv->color; -} - -/** - * gtd_task_list_view_set_color: - * @self: a #GtdTaskListView - * @color: (nullable): a #GdkRGBA - * - * Sets the custom color of @self to @color. If a custom color is set, - * the tasklist's color is ignored. Passing %NULL makes the tasklist's - * color apply again. - */ -void -gtd_task_list_view_set_color (GtdTaskListView *self, - GdkRGBA *color) -{ - GtdTaskListViewPrivate *priv; - - g_return_if_fail (GTD_IS_TASK_LIST_VIEW (self)); - - priv = gtd_task_list_view_get_instance_private (self); - - if (priv->color != color || - (color && priv->color && !gdk_rgba_equal (color, priv->color))) - { - g_clear_pointer (&priv->color, gdk_rgba_free); - priv->color = gdk_rgba_copy (color); - - on_task_list_color_changed_cb (self); - - g_object_notify (G_OBJECT (self), "color"); - } -} - -/** * gtd_task_list_view_get_handle_subtasks: * @self: a #GtdTaskListView * @@ -1616,51 +967,3 @@ gtd_task_list_view_set_handle_subtasks (GtdTaskListView *self, g_object_notify (G_OBJECT (self), "handle-subtasks"); } - - -GtdTaskListSelectorBehavior -gtd_task_list_view_get_task_list_selector_behavior (GtdTaskListView *self) -{ - GtdTaskListViewPrivate *priv; - - g_return_val_if_fail (GTD_IS_TASK_LIST_VIEW (self), -1); - - priv = gtd_task_list_view_get_instance_private (self); - - return priv->task_list_selector_behavior; -} - -void -gtd_task_list_view_set_task_list_selector_behavior (GtdTaskListView *self, - GtdTaskListSelectorBehavior behavior) -{ - GtdTaskListViewPrivate *priv; - - g_return_if_fail (GTD_IS_TASK_LIST_VIEW (self)); - - priv = gtd_task_list_view_get_instance_private (self); - - if (priv->task_list_selector_behavior == behavior) - return; - - priv->task_list_selector_behavior = behavior; - - switch (behavior) - { - case GTD_TASK_LIST_SELECTOR_BEHAVIOR_AUTOMATIC: - if (priv->model) - { - gtd_new_task_row_set_show_list_selector (GTD_NEW_TASK_ROW (priv->new_task_row), - !GTD_IS_TASK_LIST (priv->model)); - } - break; - - case GTD_TASK_LIST_SELECTOR_BEHAVIOR_ALWAYS_SHOW: - gtd_new_task_row_set_show_list_selector (GTD_NEW_TASK_ROW (priv->new_task_row), TRUE); - break; - - case GTD_TASK_LIST_SELECTOR_BEHAVIOR_ALWAYS_HIDE: - gtd_new_task_row_set_show_list_selector (GTD_NEW_TASK_ROW (priv->new_task_row), FALSE); - break; - } -} diff --git a/src/gui/gtd-task-list-view.h b/src/gui/gtd-task-list-view.h index 92f7b60f..f69c8f1a 100644 --- a/src/gui/gtd-task-list-view.h +++ b/src/gui/gtd-task-list-view.h @@ -29,13 +29,6 @@ G_BEGIN_DECLS G_DECLARE_FINAL_TYPE (GtdTaskListView, gtd_task_list_view, GTD, TASK_LIST_VIEW, GtkBox) -typedef enum -{ - GTD_TASK_LIST_SELECTOR_BEHAVIOR_AUTOMATIC, - GTD_TASK_LIST_SELECTOR_BEHAVIOR_ALWAYS_SHOW, - GTD_TASK_LIST_SELECTOR_BEHAVIOR_ALWAYS_HIDE, -} GtdTaskListSelectorBehavior; - /** * GtdTaskListViewHeaderFunc: * @task: the #GtdTask that @row represents @@ -71,31 +64,16 @@ void gtd_task_list_view_set_header_func (GtdTaskListView GtdTaskListViewHeaderFunc func, gpointer user_data); -gboolean gtd_task_list_view_get_show_new_task_row (GtdTaskListView *view); - -void gtd_task_list_view_set_show_new_task_row (GtdTaskListView *view, - gboolean show_new_task_row); - GDateTime* gtd_task_list_view_get_default_date (GtdTaskListView *self); void gtd_task_list_view_set_default_date (GtdTaskListView *self, GDateTime *default_date); -GdkRGBA* gtd_task_list_view_get_color (GtdTaskListView *self); - -void gtd_task_list_view_set_color (GtdTaskListView *self, - GdkRGBA *color); - gboolean gtd_task_list_view_get_handle_subtasks (GtdTaskListView *self); void gtd_task_list_view_set_handle_subtasks (GtdTaskListView *self, gboolean handle_subtasks); -GtdTaskListSelectorBehavior gtd_task_list_view_get_task_list_selector_behavior (GtdTaskListView *self); - -void gtd_task_list_view_set_task_list_selector_behavior (GtdTaskListView *self, - GtdTaskListSelectorBehavior behavior); - G_END_DECLS #endif /* GTD_TASK_LIST_VIEW_H */ diff --git a/src/gui/gtd-task-list-view.ui b/src/gui/gtd-task-list-view.ui index 2497f70b..cd22cbfd 100644 --- a/src/gui/gtd-task-list-view.ui +++ b/src/gui/gtd-task-list-view.ui @@ -2,76 +2,68 @@ <interface> <requires lib="gtk+" version="3.16"/> <template class="GtdTaskListView" parent="GtkBox"> - <property name="vexpand">1</property> + <property name="vexpand">true</property> <property name="orientation">vertical</property> <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="stack"> + <property name="hexpand">true</property> + <property name="vexpand">true</property> + <property name="transition-type">crossfade</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="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">listview</property> + <property name="child"> + <object class="GtdWidget"> + <property name="hexpand">true</property> + <property name="vexpand">true</property> + <child> + <object class="GtkBox"> + <property name="orientation">vertical</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> + <object class="GtkScrolledWindow" id="scrolled_window"> + <property name="can_focus">true</property> + <property name="hexpand">true</property> + <property name="vexpand">true</property> + <property name="min-content-height">320</property> + <property name="hscrollbar-policy">never</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"/> + <object class="GtkListView" id="listview"> + <property name="margin-top">6</property> + <property name="margin-start">18</property> + <property name="margin-end">18</property> + <property name="hexpand">true</property> + <property name="single-click-activate">true</property> + <property name="factory"> + <object class="GtkSignalListItemFactory"> + <signal name="setup" handler="on_listview_setup_cb" object="GtdTaskListView" swapped="no" /> + <signal name="bind" handler="on_listview_bind_cb" object="GtdTaskListView" swapped="no" /> + </object> + </property> + <signal name="activate" handler="on_listview_activate_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> - - </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> + <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> diff --git a/src/gui/gtd-task-row.c b/src/gui/gtd-task-row.c index 876df1bd..ae6c2a0a 100644 --- a/src/gui/gtd-task-row.c +++ b/src/gui/gtd-task-row.c @@ -72,6 +72,7 @@ struct _GtdTaskRow gboolean active; gboolean changed; + gboolean pressed; }; #define PRIORITY_ICON_SIZE 8 @@ -293,8 +294,6 @@ on_drag_end_cb (GtkDragSource *source, { GTD_ENTRY; - gtd_task_row_unset_drag_offset (self); - gtk_widget_set_cursor_from_name (GTK_WIDGET (self), NULL); gtk_widget_show (GTK_WIDGET (self)); @@ -309,8 +308,6 @@ on_drag_cancelled_cb (GtkDragSource *source, { GTD_ENTRY; - gtd_task_row_unset_drag_offset (self); - gtk_widget_set_cursor_from_name (GTK_WIDGET (self), NULL); gtk_widget_show (GTK_WIDGET (self)); @@ -426,6 +423,54 @@ on_task_changed_cb (GtdTaskRow *self) self->changed = TRUE; } +static void +on_click_gesture_pressed_cb (GtkGestureClick *gesture, + gint n_press, + gdouble x, + gdouble y, + GtdTaskRow *self) +{ + GTD_ENTRY; + + if (self->pressed || n_press != 1) + GTD_RETURN (); + + self->pressed = TRUE; + + GTD_EXIT; +} + + +static void +on_click_gesture_released_cb (GtkGestureClick *gesture, + gint n_press, + gdouble x, + gdouble y, + GtdTaskRow *self) +{ + GTD_ENTRY; + + if (!self->pressed || n_press != 1) + GTD_RETURN (); + + gtd_task_row_set_active (self, !self->active); + + self->pressed = FALSE; + + GTD_EXIT; +} + +static void +on_click_gesture_stopped_cb (GtkGestureClick *gesture, + GtdTaskRow *self) +{ + GTD_ENTRY; + + self->pressed = FALSE; + + GTD_EXIT; +} + /* * GObject overrides @@ -642,6 +687,9 @@ gtd_task_row_class_init (GtdTaskRowClass *klass) gtk_widget_class_bind_template_child (widget_class, GtdTaskRow, title_entry); gtk_widget_class_bind_template_callback (widget_class, on_button_press_event_cb); + gtk_widget_class_bind_template_callback (widget_class, on_click_gesture_pressed_cb); + gtk_widget_class_bind_template_callback (widget_class, on_click_gesture_released_cb); + gtk_widget_class_bind_template_callback (widget_class, on_click_gesture_stopped_cb); gtk_widget_class_bind_template_callback (widget_class, on_complete_check_toggled_cb); gtk_widget_class_bind_template_callback (widget_class, on_key_pressed_cb); gtk_widget_class_bind_template_callback (widget_class, on_remove_task_cb); @@ -695,6 +743,9 @@ gtd_task_row_set_task (GtdTaskRow *self, g_return_if_fail (GTD_IS_TASK_ROW (self)); + if (task == self->task) + return; + old_task = self->task; if (old_task) @@ -845,7 +896,8 @@ gtd_task_row_set_handle_subtasks (GtdTaskRow *self, gtk_widget_set_visible (self->dnd_box, handle_subtasks); gtk_widget_set_visible (self->dnd_icon, handle_subtasks); - on_depth_changed_cb (self, NULL, self->task); + if (self->task) + on_depth_changed_cb (self, NULL, self->task); g_object_notify (G_OBJECT (self), "handle-subtasks"); } @@ -962,32 +1014,3 @@ gtd_task_row_set_drag_offset (GtdTaskRow *self, gtk_widget_show (self->dnd_frame); } - -void -gtd_task_row_unset_drag_offset (GtdTaskRow *self) -{ - g_return_if_fail (GTD_IS_TASK_ROW (self)); - - gtk_widget_hide (self->dnd_frame); -} - -GtdTask* -gtd_task_row_get_dnd_drop_task (GtdTaskRow *self) -{ - GtdTask *task; - gint task_depth; - gint depth; - gint i; - - g_return_val_if_fail (GTD_IS_TASK_ROW (self), NULL); - - task = self->task; - task_depth = gtd_task_get_depth (task); - depth = (gtk_widget_get_margin_start (self->dnd_frame) - 12) / 32; - - /* Find the real parent */ - for (i = task_depth - depth; i >= 0; i--) - task = gtd_task_get_parent (task); - - return task; -} diff --git a/src/gui/gtd-task-row.h b/src/gui/gtd-task-row.h index b51099ba..851c652f 100644 --- a/src/gui/gtd-task-row.h +++ b/src/gui/gtd-task-row.h @@ -54,16 +54,6 @@ void gtd_task_row_set_sizegroups (GtdTaskRow GtkSizeGroup *name_group, GtkSizeGroup *date_group); -gint gtd_task_row_get_x_offset (GtdTaskRow *self); - -void gtd_task_row_set_drag_offset (GtdTaskRow *self, - GtdTaskRow *source_row, - gint x_offset); - -void gtd_task_row_unset_drag_offset (GtdTaskRow *self); - -GtdTask* gtd_task_row_get_dnd_drop_task (GtdTaskRow *self); - G_END_DECLS #endif /* GTD_TASK_ROW_H */ diff --git a/src/gui/gtd-task-row.ui b/src/gui/gtd-task-row.ui index ffa6860e..c50349bb 100644 --- a/src/gui/gtd-task-row.ui +++ b/src/gui/gtd-task-row.ui @@ -3,6 +3,12 @@ <requires lib="gtk+" version="3.16"/> <template class="GtdTaskRow" parent="GtdWidget"> <property name="hexpand">true</property> + <property name="halign">center</property> + <property name="layout-manager"> + <object class="GtdMaxSizeLayout"> + <property name="max-width">700</property> + </object> + </property> <child> <object class="GtkEventControllerKey"> <property name="propagation-phase">capture</property> @@ -10,6 +16,14 @@ </object> </child> <child> + <object class="GtkGestureClick"> + <property name="propagation-phase">bubble</property> + <signal name="pressed" handler="on_click_gesture_pressed_cb" object="GtdTaskRow" swapped="no"/> + <signal name="released" handler="on_click_gesture_released_cb" object="GtdTaskRow" swapped="no"/> + <signal name="stopped" handler="on_click_gesture_stopped_cb" object="GtdTaskRow" swapped="no"/> + </object> + </child> + <child> <object class="GtkBox" id="main_box"> <property name="orientation">vertical</property> <child> diff --git a/src/meson.build b/src/meson.build index bea40f5b..e8df5685 100644 --- a/src/meson.build +++ b/src/meson.build @@ -141,6 +141,7 @@ sources += files( 'gui/gtd-new-task-row.c', 'gui/gtd-task-list-popover.c', 'gui/gtd-task-list-view.c', + 'gui/gtd-task-list-view-model.c', 'gui/gtd-task-row.c', 'gui/gtd-color-button.c', 'gui/gtd-empty-list-widget.c', diff --git a/src/plugins/inbox-panel/gtd-inbox-panel.c b/src/plugins/inbox-panel/gtd-inbox-panel.c index 62272780..04e61bea 100644 --- a/src/plugins/inbox-panel/gtd-inbox-panel.c +++ b/src/plugins/inbox-panel/gtd-inbox-panel.c @@ -254,8 +254,6 @@ gtd_inbox_panel_init (GtdInboxPanel *self) gtd_task_list_view_set_handle_subtasks (GTD_TASK_LIST_VIEW (self->view), FALSE); gtd_task_list_view_set_show_list_name (GTD_TASK_LIST_VIEW (self->view), FALSE); gtd_task_list_view_set_show_due_date (GTD_TASK_LIST_VIEW (self->view), FALSE); - gtd_task_list_view_set_task_list_selector_behavior (GTD_TASK_LIST_VIEW (self->view), - GTD_TASK_LIST_SELECTOR_BEHAVIOR_ALWAYS_HIDE); gtk_widget_set_hexpand (GTK_WIDGET (self->view), TRUE); gtk_widget_set_vexpand (GTK_WIDGET (self->view), TRUE); |