[gnome-todo/wip/gbsneto/subtasks: 14/37] dnd-row: introduce GtdDndRow
- From: Georges Basile Stavracas Neto <gbsneto src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-todo/wip/gbsneto/subtasks: 14/37] dnd-row: introduce GtdDndRow
- Date: Thu, 20 Oct 2016 15:11:16 +0000 (UTC)
commit fa6b8ff668fcbdb8d6ad517aa4fccca527b5f0dd
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date: Fri Oct 14 01:29:16 2016 -0300
dnd-row: introduce GtdDndRow
A row specialized in creating chaos with DnD
data/theme/Adwaita.css | 6 +
data/todo.gresource.xml | 1 +
data/ui/dnd-row.ui | 45 ++++++
doc/reference/Makefile.am | 1 +
src/Makefile.am | 8 +-
src/gtd-dnd-row.c | 331 +++++++++++++++++++++++++++++++++++++++++++++
src/gtd-dnd-row.h | 49 +++++++
7 files changed, 440 insertions(+), 1 deletions(-)
---
diff --git a/data/theme/Adwaita.css b/data/theme/Adwaita.css
index 3b45d67..3e9959f 100644
--- a/data/theme/Adwaita.css
+++ b/data/theme/Adwaita.css
@@ -103,6 +103,12 @@ taskrow.complete label {
text-decoration-line: line-through;
}
+/* dnd row */
+dndrow frame {
+ background: alpha(#333333, 0.5);
+ min-height: 36px;
+}
+
/* extension list */
list.extension-list row {
border-bottom: solid 1px @borders
diff --git a/data/todo.gresource.xml b/data/todo.gresource.xml
index a4d8302..e4a4c04 100644
--- a/data/todo.gresource.xml
+++ b/data/todo.gresource.xml
@@ -2,6 +2,7 @@
<gresources>
<gresource prefix="/org/gnome/todo">
<file alias="gtk/menus.ui">gtk/menus.ui</file>
+ <file compressed="true" preprocess="xml-stripblanks">ui/dnd-row.ui</file>
<file compressed="true" preprocess="xml-stripblanks">ui/edit-pane.ui</file>
<file compressed="true" preprocess="xml-stripblanks">ui/initial-setup.ui</file>
<file compressed="true" preprocess="xml-stripblanks">ui/list-selector-grid-item.ui</file>
diff --git a/data/ui/dnd-row.ui b/data/ui/dnd-row.ui
new file mode 100644
index 0000000..c94544f
--- /dev/null
+++ b/data/ui/dnd-row.ui
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="3.20"/>
+ <template class="GtdDndRow" parent="GtkListBoxRow">
+ <property name="can_focus">True</property>
+ <property name="activatable">False</property>
+ <property name="selectable">False</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_start">24</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkImage" id="icon">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">open-menu-symbolic</property>
+ <property name="pixel-size">12</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkFrame">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="margin_end">24</property>
+ <property name="margin_top">24</property>
+ <property name="margin_bottom">24</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/doc/reference/Makefile.am b/doc/reference/Makefile.am
index 09dfedc..41159c5 100644
--- a/doc/reference/Makefile.am
+++ b/doc/reference/Makefile.am
@@ -56,6 +56,7 @@ CFILE_GLOB = \
IGNORE_HFILES = \
gtd-application.h \
gtd-arrow-frame.h \
+ gtd-dnd-row.h \
gtd-edit-pane.h \
gtd-enum-types.h \
gtd-initial-setup-window.h \
diff --git a/src/Makefile.am b/src/Makefile.am
index bfa8ef7..749435d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -62,6 +62,8 @@ gnome_todo_SOURCES = \
gtd-application.h \
gtd-arrow-frame.c \
gtd-arrow-frame.h \
+ gtd-dnd-row.c \
+ gtd-dnd-row.h \
gtd-edit-pane.c \
gtd-edit-pane.h \
gtd-enums.h \
@@ -96,6 +98,7 @@ gnome_todo_LDFLAGS = \
-Wl,--undefined=gtd_plugin_dark_theme_register_types \
-Wl,--undefined=gtd_plugin_scheduled_panel_register_types \
-Wl,--undefined=gtd_plugin_today_panel_register_types \
+ -lm \
$(GNOME_TODO_WARN_LDFLAGS)
gnome_todo_LDADD = \
@@ -206,6 +209,8 @@ libgtd_la_SOURCES = \
gtd-application.h \
gtd-arrow-frame.c \
gtd-arrow-frame.h \
+ gtd-dnd-row.c \
+ gtd-dnd-row.h \
gtd-edit-pane.c \
gtd-edit-pane.h \
gtd-enum-types.c \
@@ -236,7 +241,8 @@ libgtd_la_CFLAGS = \
$(GNOME_TODO_WARN_CFLAGS)
libgtd_la_LDFLAGS = \
- -Wl,--export-dynamic
+ -Wl,--export-dynamic \
+ -lm
endif
diff --git a/src/gtd-dnd-row.c b/src/gtd-dnd-row.c
new file mode 100644
index 0000000..d6aa245
--- /dev/null
+++ b/src/gtd-dnd-row.c
@@ -0,0 +1,331 @@
+/* gtd-dnd-row.c
+ *
+ * Copyright (C) 2016 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/>.
+ */
+
+#include "gtd-dnd-row.h"
+#include "gtd-provider.h"
+#include "gtd-task.h"
+#include "gtd-task-list.h"
+#include "gtd-task-row.h"
+
+#include <math.h>
+
+struct _GtdDndRow
+{
+ GtkListBoxRow parent;
+
+ GtkWidget *icon;
+
+ GtdTaskRow *row_above;
+ GtdTask *source_task;
+ gint depth;
+ gboolean has_dnd : 1;
+};
+
+G_DEFINE_TYPE (GtdDndRow, gtd_dnd_row, GTK_TYPE_LIST_BOX_ROW)
+
+enum {
+ PROP_0,
+ PROP_ROW_ABOVE,
+ N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+static GtdTask*
+get_real_task_for_depth (GtdDndRow *self)
+{
+ GtdTask *task;
+ gint i, task_depth;
+
+ task = self->row_above ? gtd_task_row_get_task (self->row_above) : NULL;
+ task_depth = task ? gtd_task_get_depth (task) : -1;
+
+ /* Find the real parent */
+ for (i = task_depth - self->depth; i >= 0; i--)
+ task = gtd_task_get_parent (task);
+
+ return task;
+}
+
+static void
+update_row_padding (GtdDndRow *self)
+{
+ gtk_widget_set_margin_start (self->icon, self->depth * 32);
+}
+
+static void
+gtd_dnd_row_finalize (GObject *object)
+{
+ GtdDndRow *self = (GtdDndRow *)object;
+
+ g_clear_object (&self->row_above);
+
+ G_OBJECT_CLASS (gtd_dnd_row_parent_class)->finalize (object);
+}
+
+static void
+gtd_dnd_row_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtdDndRow *self = GTD_DND_ROW (object);
+
+ switch (prop_id)
+ {
+ case PROP_ROW_ABOVE:
+ g_value_set_object (value, self->row_above);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gtd_dnd_row_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtdDndRow *self = GTD_DND_ROW (object);
+
+ switch (prop_id)
+ {
+ case PROP_ROW_ABOVE:
+ gtd_dnd_row_set_row_above (self, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gtd_dnd_row_drag_leave (GtkWidget *widget,
+ GdkDragContext *context,
+ guint time)
+{
+ GtdDndRow *self = GTD_DND_ROW (widget);
+
+ self->has_dnd = FALSE;
+}
+
+static gboolean
+gtd_dnd_row_drag_motion (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time)
+{
+ GtdDndRow *self;
+
+ self = GTD_DND_ROW (widget);
+
+ if (self->row_above)
+ {
+ GtdTask *task;
+
+ task = gtd_task_row_get_task (self->row_above);
+ self->depth = CLAMP (floor (x / 32), 0, gtd_task_get_depth (task) + 1);
+ }
+ else
+ {
+ self->depth = 0;
+ }
+
+ self->has_dnd = TRUE;
+
+ update_row_padding (self);
+
+ gdk_drag_status (context, GDK_ACTION_COPY, time);
+
+ return TRUE;
+}
+
+static gboolean
+gtd_dnd_row_drag_drop (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time)
+{
+ GtdProvider *provider;
+ GtdDndRow *self;
+ GtkWidget *source_widget, *row;
+ GtdTask *row_task, *target_task;
+
+ self = GTD_DND_ROW (widget);
+
+ /* Reset padding */
+ update_row_padding (self);
+
+ gtk_widget_hide (widget);
+
+ row = NULL;
+ source_widget = gtk_drag_get_source_widget (context);
+
+ if (!source_widget)
+ {
+ gdk_drag_status (context, 0, time);
+ return FALSE;
+ }
+
+ /*
+ * When the drag operation began, the source row was hidden. Now is the time
+ * to show it again.
+ */
+ row = gtk_widget_get_ancestor (source_widget, GTD_TYPE_TASK_ROW);
+ gtk_widget_show (row);
+
+ /* Do not allow dropping on itself, nor on the new task row */
+ if (!row || row == widget || gtd_task_row_get_new_task_mode (GTD_TASK_ROW (row)))
+ {
+ gdk_drag_status (context, 0, time);
+ return FALSE;
+ }
+
+ row_task = gtd_task_row_get_task (GTD_TASK_ROW (row));
+ target_task = get_real_task_for_depth (self);
+
+ if (target_task)
+ {
+ /* Forbid adding the parent task as a subtask */
+ if (gtd_task_is_subtask (row_task, target_task))
+ {
+ gdk_drag_status (context, 0, time);
+ return FALSE;
+ }
+
+ gtd_task_add_subtask (target_task, row_task);
+ }
+ else
+ {
+ /*
+ * If the user moved to depth == 0, or the first row,
+ * remove the task from it's parent (if any).
+ */
+ if (gtd_task_get_parent (row_task))
+ gtd_task_remove_subtask (gtd_task_get_parent (row_task), row_task);
+ }
+
+ /* Save the task */
+ provider = gtd_task_list_get_provider (gtd_task_get_list (row_task));
+
+ gtd_task_save (row_task);
+ gtd_provider_update_task (provider, row_task);
+
+ gtk_list_box_invalidate_sort (GTK_LIST_BOX (gtk_widget_get_parent (widget)));
+
+ return TRUE;
+}
+
+static void
+gtd_dnd_row_class_init (GtdDndRowClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gtd_dnd_row_finalize;
+ object_class->get_property = gtd_dnd_row_get_property;
+ object_class->set_property = gtd_dnd_row_set_property;
+
+ widget_class->drag_drop = gtd_dnd_row_drag_drop;
+ widget_class->drag_leave = gtd_dnd_row_drag_leave;
+ widget_class->drag_motion = gtd_dnd_row_drag_motion;
+
+ properties[PROP_ROW_ABOVE] = g_param_spec_object ("row-above",
+ "Row above",
+ "The task row above this row",
+ GTD_TYPE_TASK_ROW,
+ G_PARAM_READWRITE);
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/todo/ui/dnd-row.ui");
+
+ gtk_widget_class_bind_template_child (widget_class, GtdDndRow, icon);
+
+ gtk_widget_class_set_css_name (widget_class, "dndrow");
+}
+
+static void
+gtd_dnd_row_init (GtdDndRow *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ gtk_drag_dest_set (GTK_WIDGET (self),
+ 0,
+ NULL,
+ 0,
+ GDK_ACTION_MOVE);
+}
+
+GtkWidget*
+gtd_dnd_row_new (void)
+{
+ return g_object_new (GTD_TYPE_DND_ROW, NULL);
+}
+
+GtdTaskRow*
+gtd_dnd_row_get_row_above (GtdDndRow *self)
+{
+ g_return_val_if_fail (GTD_IS_DND_ROW (self), NULL);
+
+ return self->row_above;
+}
+
+void
+gtd_dnd_row_set_row_above (GtdDndRow *self,
+ GtdTaskRow *row)
+{
+ g_return_if_fail (GTD_IS_DND_ROW (self));
+
+ if (g_set_object (&self->row_above, row))
+ {
+ update_row_padding (self);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ROW_ABOVE]);
+ }
+}
+
+GtdTask*
+gtd_dnd_row_get_source_task (GtdDndRow *self)
+{
+ g_return_val_if_fail (GTD_IS_DND_ROW (self), NULL);
+
+ return self->source_task;
+}
+
+void
+gtd_dnd_row_set_source_task (GtdDndRow *self,
+ GtdTask *source_task)
+{
+ g_return_if_fail (GTD_IS_DND_ROW (self));
+
+ g_set_object (&self->source_task, source_task);
+}
+
+gboolean
+gtd_dnd_row_has_dnd (GtdDndRow *self)
+{
+ g_return_val_if_fail (GTD_IS_DND_ROW (self), FALSE);
+
+ return self->has_dnd;
+}
+
diff --git a/src/gtd-dnd-row.h b/src/gtd-dnd-row.h
new file mode 100644
index 0000000..e55d466
--- /dev/null
+++ b/src/gtd-dnd-row.h
@@ -0,0 +1,49 @@
+/* gtd-dnd-row.h
+ *
+ * Copyright (C) 2016 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/>.
+ */
+
+#ifndef GTD_DND_ROW_H
+#define GTD_DND_ROW_H
+
+#include "gtd-types.h"
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GTD_TYPE_DND_ROW (gtd_dnd_row_get_type())
+
+G_DECLARE_FINAL_TYPE (GtdDndRow, gtd_dnd_row, GTD, DND_ROW, GtkListBoxRow)
+
+GtkWidget* gtd_dnd_row_new (void);
+
+GtdTaskRow* gtd_dnd_row_get_row_above (GtdDndRow *self);
+
+void gtd_dnd_row_set_row_above (GtdDndRow *self,
+ GtdTaskRow *row);
+
+GtdTask* gtd_dnd_row_get_source_task (GtdDndRow *self);
+
+void gtd_dnd_row_set_source_task (GtdDndRow *self,
+ GtdTask *source_task);
+
+gboolean gtd_dnd_row_has_dnd (GtdDndRow *self);
+
+G_END_DECLS
+
+#endif /* GTD_DND_ROW_H */
+
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]