[rkward/work/optionset_experiments] rkward: Implement drag-and-drop item reordering in accordion.

Thomas Friedrichsmeier thomas.friedrichsmeier at ruhr-uni-bochum.de
Wed Oct 28 13:48:47 UTC 2015


Git commit 46b186da7766be3eec3284d87f2f5c2cc238d8f2 by Thomas Friedrichsmeier.
Committed on 28/10/2015 at 13:47.
Pushed by tfry into branch 'work/optionset_experiments'.

Implement drag-and-drop item reordering in accordion.
This should obsolete the per-row "add" buttons.

M  +27   -7    rkward/misc/rkaccordiontable.cpp
M  +61   -4    rkward/plugin/rkoptionset.cpp
M  +7    -0    rkward/plugin/rkoptionset.h
M  +1    -0    rkward/plugins/testing/optionset.js

http://commits.kde.org/rkward/46b186da7766be3eec3284d87f2f5c2cc238d8f2

diff --git a/rkward/misc/rkaccordiontable.cpp b/rkward/misc/rkaccordiontable.cpp
index 5aa0803..604ed2b 100644
--- a/rkward/misc/rkaccordiontable.cpp
+++ b/rkward/misc/rkaccordiontable.cpp
@@ -72,7 +72,7 @@ public:
 
 	Qt::ItemFlags flags (const QModelIndex& index) const {
 		if (isFake (index)) return (Qt::NoItemFlags);
-		return QAbstractProxyModel::flags (index);
+		return (QAbstractProxyModel::flags (index));
 	}
 
 	int rowCount (const QModelIndex& parent = QModelIndex ()) const {
@@ -81,11 +81,23 @@ public:
 		return sourceModel ()->rowCount (mapToSource (parent)) + add_trailing_rows;
 	}
 
-    QVariant data(const QModelIndex& proxyIndex, int role = Qt::DisplayRole) const {
+    QVariant data (const QModelIndex& proxyIndex, int role = Qt::DisplayRole) const {
 		if (isFake (proxyIndex)) return QVariant ();
 		return QAbstractProxyModel::data (proxyIndex, role);
 	}
 
+	bool dropMimeData (const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) {
+		// Ok, I don't understand why exactly, but something goes wrong while mapping this back to the source model. So we help it a bit:
+		Q_UNUSED (column);
+
+		if (isFake (parent)) {
+			RK_ASSERT (false);
+			return false;
+		}
+		if (parent.isValid ()) row = parent.row ();
+		return sourceModel ()->dropMimeData (data, action, row, 0, QModelIndex ());
+	}
+
 	QVariant headerData (int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const {
 		if ((orientation == Qt::Horizontal) && (section < add_leading_columns) && (role == Qt::DisplayRole)) return QVariant ();
 		return QAbstractProxyModel::headerData (section, orientation, role);
@@ -105,7 +117,7 @@ public:
 			if (row == sourceModel ()->rowCount ()) return createIndex (row, column, trailing_item_id);
 			return createIndex (row, column, real_item_id);
 		}
-		RK_ASSERT (parent.internalId () == real_item_id);
+		RK_ASSERT (parent.internalId () >= trailing_item_id);
 		return createIndex (row, column, parent.row ());
 	}
 
@@ -199,7 +211,12 @@ RKAccordionTable::RKAccordionTable (QWidget* parent) : QTreeView (parent) {
 	editor_widget = new KVBox (editor_widget_container);
 	layout->addWidget (editor_widget);
 
-	setSelectionMode (NoSelection);
+	setSelectionMode (SingleSelection);
+	setDragEnabled (true);
+	setAcceptDrops (true);
+	setDragDropMode (InternalMove);
+	setDropIndicatorShown (false);
+	setDragDropOverwriteMode (false);
 	setIndentation (0);
 	setRootIsDecorated (false);
 	setExpandsOnDoubleClick (false);   // we expand on single click, instead
@@ -274,8 +291,12 @@ void RKAccordionTable::rowClicked (QModelIndex row) {
 	RK_TRACE (MISC);
 
 	row = model ()->index (row.row (), 0, row.parent ());   // Fix up index to point to column 0, or isExpanded() will always return false
-	if (!row.parent ().isValid ()) {
-		setExpanded (row, !isExpanded (row));
+	if (isExpanded (row)) {
+		setExpanded (row, false);
+	} else {
+		if (!row.parent ().isValid ()) {
+			emit (activated (row.row ()));
+		}
 	}
 }
 
@@ -293,7 +314,6 @@ void RKAccordionTable::rowExpanded (QModelIndex row) {
 	setCurrentIndex (row);
 	scrollTo (row, EnsureVisible);                          // yes, we want both scrolls: We want the header row above the widget, if possible at all,
 	scrollTo (model ()->index (0, 0, row), EnsureVisible);  // but of course, having the header row visible without the widget is not enough...
-	emit (activated (row.row ()));
 }
 
 void RKAccordionTable::updateWidget () {
diff --git a/rkward/plugin/rkoptionset.cpp b/rkward/plugin/rkoptionset.cpp
index 00fa92e..056c69d 100644
--- a/rkward/plugin/rkoptionset.cpp
+++ b/rkward/plugin/rkoptionset.cpp
@@ -23,6 +23,7 @@
 #include <QPushButton>
 #include <QStackedWidget>
 #include <QLabel>
+#include <QMimeData>
 
 #include <klocale.h>
 #include <kvbox.h>
@@ -429,9 +430,9 @@ void RKOptionSet::addRow () {
 
 void RKOptionSet::addRow (int row) {
 	RK_TRACE (PLUGIN);
-
+RK_DEBUG (MISC, DL_ERROR, "current row is now %d, %d", current_row->intValue (), active_row);
 	storeRowSerialization (active_row);
-
+RK_DEBUG (MISC, DL_ERROR, "current row is nows %d, %d", current_row->intValue (), active_row);
 	int nrows = rowCount ();
 	if (row < 0) row = nrows;
 	RK_ASSERT (!keycolumn);
@@ -456,7 +457,6 @@ void RKOptionSet::addRow (int row) {
 	rows.insert (row, ri);
 	++n_unfinished_rows;
 	++n_invalid_rows;
-
 	row_count->setIntValue (nrows + 1);
 	current_row->setIntValue (active_row = row);
 	setContentsForRow (active_row);
@@ -508,6 +508,31 @@ void RKOptionSet::removeRow (int row) {
 	changed ();
 }
 
+void RKOptionSet::moveRow (int old_index, int new_index) {
+	RK_TRACE (PLUGIN);
+
+	int nrows = rowCount ();
+	if (old_index < 0 || old_index >= nrows) {
+		RK_ASSERT (false);
+		return;
+	}
+
+	if (new_index < 0 || new_index > nrows) {
+		new_index = nrows;
+	}
+
+	storeRowSerialization (active_row);
+	PropertyValueMap backup = rows[old_index].full_row_map;
+	removeRow (old_index);
+	if (new_index > old_index) new_index -= 1;
+	addRow (new_index);
+	rows[new_index].full_row_map = backup;
+	setContentsForRow (new_index);
+	updateCurrentRowInDisplay ();
+
+	changed ();
+}
+
 QString getDefaultValue (const RKOptionSet::ColumnInfo& ci, int row) {
 	// let's not trace this simple helper fun
 	Q_UNUSED (row);
@@ -789,7 +814,7 @@ void RKOptionSet::currentRowChanged () {
 void RKOptionSet::currentRowChanged (int row) {
 	RK_TRACE (PLUGIN);
 
-	if (active_row != row) current_row->setIntValue (row);
+	current_row->setIntValue (row);
 	// --> currentRowPropertyChanged ()
 }
 
@@ -836,6 +861,7 @@ RKOptionSetDisplayModel::RKOptionSetDisplayModel (RKOptionSet* parent) : QAbstra
 	connect (&reset_timer, SIGNAL (timeout()), this, SLOT (doResetNow()));
 	reset_timer.setInterval (0);
 	reset_timer.setSingleShot (true);
+	setSupportedDragActions (Qt::MoveAction);
 }
 
 RKOptionSetDisplayModel::~RKOptionSetDisplayModel () {
@@ -914,6 +940,37 @@ void RKOptionSetDisplayModel::triggerReset() {
 	}
 }
 
+QString optionsetdisplaymodel_mt ("application/x-rkaccordiontableitem");
+QStringList RKOptionSetDisplayModel::mimeTypes () const {
+	return QStringList (optionsetdisplaymodel_mt);
+}
+
+QMimeData* RKOptionSetDisplayModel::mimeData (const QModelIndexList& indexes) const {
+	RK_ASSERT (indexes.length () >= 1);
+	QMimeData *ret = new QMimeData ();
+	ret->setData (optionsetdisplaymodel_mt, QByteArray (QString::number (indexes.first ().row ()).toAscii ()));
+	return (ret);
+}
+
+bool RKOptionSetDisplayModel::dropMimeData (const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) {
+	Q_UNUSED (column);
+	if (action == Qt::IgnoreAction) return true;
+	if (action == Qt::MoveAction) {
+		if (parent.isValid ()) return false;
+		int srow = QString::fromAscii (data->data (optionsetdisplaymodel_mt)).toInt ();
+		set->moveRow (srow, row);
+	}
+	return false;
+}
+
+Qt::ItemFlags RKOptionSetDisplayModel::flags (const QModelIndex& index) const {
+	return QAbstractItemModel::flags (index) | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
+}
+
+Qt::DropActions RKOptionSetDisplayModel::supportedDropActions () const {
+    return Qt::MoveAction;
+}
+
 #include <QApplication>
 RKOptionSetDelegate::RKOptionSetDelegate (RKOptionSet* parent) : QItemDelegate (parent) {
 	set = parent;
diff --git a/rkward/plugin/rkoptionset.h b/rkward/plugin/rkoptionset.h
index ad52247..48f5902 100644
--- a/rkward/plugin/rkoptionset.h
+++ b/rkward/plugin/rkoptionset.h
@@ -70,6 +70,7 @@ friend class RKOptionSetDisplayModel;
 	void setRowState (int row, bool finished, bool valid);
 	void storeRowSerialization (int row);
 	void applyContentsFromExternalColumn (RKComponentPropertyStringList* column, int row);
+	void moveRow (int old_index, int new_index);
 
 	RKComponentPropertyInt *current_row;
 	RKComponentPropertyInt *row_count;
@@ -161,6 +162,12 @@ friend class RKOptionSet;
 	QTimer reset_timer;
 	QStringList column_labels;
 	RKOptionSet *set;
+
+	QMimeData* mimeData (const QModelIndexList& indexes) const;
+	QStringList mimeTypes () const;
+	bool dropMimeData (const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent);
+	Qt::ItemFlags flags (const QModelIndex& index) const;
+	Qt::DropActions supportedDropActions () const;
 private slots:
 	void doResetNow ();
 };
diff --git a/rkward/plugins/testing/optionset.js b/rkward/plugins/testing/optionset.js
index c77e16b..5fbe270 100644
--- a/rkward/plugins/testing/optionset.js
+++ b/rkward/plugins/testing/optionset.js
@@ -7,6 +7,7 @@ function calculate () {
 	echo ("row_count <- " + getValue ("set.row_count") + "\n");
 	echo ("current_row <- " + getValue ("set.current_row") + "\n");
 	echo ("set.contents.enabled <- " + getValue ("set.contents.enabled") + "\n");
+	echo (getList ("mset.summary").join (",") + "\n");
 
 	var codeprops = getList ("set.plotoption_printout");
 	for (i = 0; i < codeprops.length; ++i) {



More information about the rkward-tracker mailing list