[rkward/work/optionset_experiments] rkward: First (incomplete, rough, buggy) version of RKAccordionTable.

Thomas Friedrichsmeier thomas.friedrichsmeier at ruhr-uni-bochum.de
Sun Oct 25 17:33:03 UTC 2015


Git commit 6178ba58dab721c5de8a58846c58bad8bf85a1ca by Thomas Friedrichsmeier.
Committed on 25/10/2015 at 17:31.
Pushed by tfry into branch 'work/optionset_experiments'.

First (incomplete, rough, buggy) version of RKAccordionTable.

Now internally based on QTreeView instead of QTableView, as this seems more suited for the task, after all.

M  +2    -0    rkward/misc/CMakeLists.txt
M  +129  -31   rkward/misc/rkaccordiontable.cpp
M  +5    -4    rkward/misc/rkaccordiontable.h
M  +2    -2    rkward/plugin/rkoptionset.cpp
M  +1    -1    rkward/plugins/testing/optionset.xml

http://commits.kde.org/rkward/6178ba58dab721c5de8a58846c58bad8bf85a1ca

diff --git a/rkward/misc/CMakeLists.txt b/rkward/misc/CMakeLists.txt
index 048a51a..6f521e2 100644
--- a/rkward/misc/CMakeLists.txt
+++ b/rkward/misc/CMakeLists.txt
@@ -30,6 +30,8 @@ SET(misc_STAT_SRCS
    )
 
 QT4_AUTOMOC(${misc_STAT_SRCS})
+# Urghs. QT4_AUTOMOC scans only the .h-file for Q_OBJECT macros. Let's hope this will be better with KF5
+QT4_GENERATE_MOC (rkaccordiontable.cpp ${CMAKE_CURRENT_BINARY_DIR}/rkaccordiontablemodel_moc.cpp)
 
 ADD_LIBRARY(misc STATIC ${misc_STAT_SRCS})
 
diff --git a/rkward/misc/rkaccordiontable.cpp b/rkward/misc/rkaccordiontable.cpp
index 2a307bf..daa8f73 100644
--- a/rkward/misc/rkaccordiontable.cpp
+++ b/rkward/misc/rkaccordiontable.cpp
@@ -17,41 +17,136 @@
 
 #include "rkaccordiontable.h"
 
+#include <QPointer>
+#include <QVBoxLayout>
+#include <QAbstractProxyModel>
 #include <kvbox.h>
 
 #include "../debug.h"
 
+/** Maps from the Optionset data model to the model used internally in the RKAccordionTable
+ *  (a dummy child item is inserted for each actual row). This item can _not_ actually be accessed
+ *  in a meaningful way. The only purpose is to provide a placeholder to expand / collapse in the view. */
 class RKAccordionDummyModel : public QAbstractProxyModel {
+	Q_OBJECT
 public:
 	RKAccordionDummyModel (QObject *parent) : QAbstractProxyModel (parent) {};
 
 	QModelIndex mapFromSource (const QModelIndex& sindex) const {
-		return (createIndex (sindex.row () * 2 + 1, sindex.column ()));
+		if (!sindex.isValid ()) return QModelIndex ();
+		// we're using Source row as "Internal ID", here. This _would_ fall on our feet when removing rows, _if_ we'd actually
+		// have to be able to map the dummy rows back to their real parents.
+		return (createIndex (sindex.row (), sindex.column (), real_item_id));
 	}
 
-    QModelIndex mapToSource (const QModelIndex& pindex) const {
-		int srow;
-		if (pindex.row () % 2) srow = (pindex.row () - 1) / 2;
-		else srow = pindex.row () / 2;
-		return (createIndex (srow, pindex.column ()));
+	QModelIndex mapToSource (const QModelIndex& pindex) const {
+		if (!pindex.isValid ()) return QModelIndex ();
+		if (pindex.internalId () != real_item_id) {
+			return sourceModel ()->index (pindex.internalId (), pindex.column ());
+		} else {
+			return sourceModel ()->index (pindex.row (), pindex.column ());
+		}
 	}
-	
-    Qt::ItemFlags flags (const QModelIndex& index) const {
-		if (index.row () % 2) return (Qt::NoItemFlags);
+
+	Qt::ItemFlags flags (const QModelIndex& index) const {
+		if (isFake (index)) return (Qt::NoItemFlags);
 		return QAbstractProxyModel::flags (index);
 	}
-	
-    int rowCount (const QModelIndex& parent) const {
-		return sourceModel ()->rowCount (parent) * 2;
+
+	int rowCount (const QModelIndex& parent) const {
+		if (isFake (parent)) return 0;
+		if (parent.isValid ()) return 1;
+		return sourceModel ()->rowCount (mapToSource (parent));
+	}
+
+    QVariant data(const QModelIndex& proxyIndex, int role = Qt::DisplayRole) const {
+		if (isFake (proxyIndex)) return QVariant ();
+		return QAbstractProxyModel::data (proxyIndex, role);
+	}
+
+	bool hasChildren (const QModelIndex& parent) const {
+		return (!isFake (parent));
+	}
+
+	int columnCount (const QModelIndex& parent) const {
+		if (isFake (parent)) return 1;
+		return sourceModel ()->columnCount (mapToSource (parent));
+	}
+
+	QModelIndex index (int row, int column, const QModelIndex& parent) const {
+		if (!parent.isValid ()) return createIndex (row, column, real_item_id);
+		RK_ASSERT (parent.internalId () == real_item_id);
+		return createIndex (row, column, parent.row ());
+	}
+
+	QModelIndex parent (const QModelIndex& child) const {
+		if (child.internalId () == real_item_id) return QModelIndex ();
+		return createIndex (child.internalId (), 0, real_item_id);
+	}
+
+	void setSourceModel (QAbstractItemModel* source_model) {
+		/* More than these would be needed for a proper proxy of any model, but in our case, we only have to support the RKOptionsetDisplayModel */
+		connect (source_model, SIGNAL (rowsInserted(const QModelIndex&,int,int)), this, SLOT (rowsInserted(QModelIndex,int,int)));
+		connect (source_model, SIGNAL (rowsRemoved(const QModelIndex&,int,int)), this, SLOT (rowsRemoved(QModelIndex,int,int)));
+		connect (source_model, SIGNAL (layoutChanged()), this, SLOT (relayLayoutChange()));
+		QAbstractProxyModel::setSourceModel (source_model);
+	}
+
+	bool isFake (const QModelIndex& index) const {
+		return (index.isValid () && (index.internalId () != real_item_id));
+	}
+
+	static const quint32 real_item_id = 0xFFFFFFFF;
+public slots:
+	void rowsInserted (const QModelIndex& parent, int start, int end) {
+		RK_TRACE (MISC);
+		RK_ASSERT (!parent.isValid ());
+
+		beginInsertRows (mapFromSource (parent), start, end);
+		endInsertRows ();
+	}
+	void rowsRemoved (const QModelIndex& parent, int start, int end) {
+		RK_TRACE (MISC);
+		RK_ASSERT (!parent.isValid ());
+
+		beginRemoveRows (mapFromSource (parent), start, end);
+		endRemoveRows ();
+	}
+	void relayLayoutChange () {
+ 		RK_DEBUG (MISC, DL_ERROR, "reset");
+		emit (layoutChanged());
+	}
+};
+
+
+/** Protects the given child widget from deletion */
+class RKWidgetGuard : public QWidget {
+public:
+	RKWidgetGuard (QWidget *parent, QWidget *widget_to_guard, QWidget *fallback_parent) : QWidget (parent) {
+		RK_TRACE (MISC);
+		RK_ASSERT (widget_to_guard);
+
+		guarded = widget_to_guard;
+		RKWidgetGuard::fallback_parent = fallback_parent;
+
+		QVBoxLayout *layout = new QVBoxLayout (this);
+		guarded->setParent (this);
+		layout->addWidget (guarded);
 	}
 
-	int columnCount(const QModelIndex& parent) const {
-		return sourceModel ()->columnCount (parent);
+	~RKWidgetGuard () {
+		RK_TRACE (MISC);
+		if ((!guarded.isNull ()) && guarded->parent () == this) {
+			guarded->setParent (fallback_parent);
+		}
 	}
+private:
+	QPointer<QWidget> guarded;
+	QWidget *fallback_parent;
 };
 
 #include <QLabel>
-RKAccordionTable::RKAccordionTable (QWidget* parent) : QTableView (parent) {
+RKAccordionTable::RKAccordionTable (QWidget* parent) : QTreeView (parent) {
 	RK_TRACE (MISC);
 
 	default_widget = new KVBox;
@@ -59,6 +154,7 @@ RKAccordionTable::RKAccordionTable (QWidget* parent) : QTableView (parent) {
 	setSelectionBehavior (SelectRows);
 	setSelectionMode (SingleSelection);
 	pmodel = new RKAccordionDummyModel (this);
+	connect (this, SIGNAL (expanded(QModelIndex)), this, SLOT (rowExpanded(QModelIndex)));
 }
 
 RKAccordionTable::~RKAccordionTable () {
@@ -70,39 +166,41 @@ RKAccordionTable::~RKAccordionTable () {
 void RKAccordionTable::currentChanged (const QModelIndex& current, const QModelIndex& previous) {
 	RK_TRACE (MISC);
 	RK_ASSERT (current.isValid ());
+	Q_UNUSED (previous);
+// TODO: needed?
+	return;
+	if (!isExpanded (current)) {
+		expand (current);
+	}
+}
 
-	int cur = current.row;
-	if (previous.isValid ()) {
-		int prev = previous.row;
-		int height = rowHeight (prev + 1);
-		hideRow (prev + 1);
-		setRowHeight (cur + 1, height);
+void RKAccordionTable::rowExpanded (QModelIndex row) {
+	RK_TRACE (MISC);
+
+	for (int i = 0; i < model ()->rowCount (); ++i) {
+		if (i != row.row ()) setExpanded (model ()->index (i, 0), false);
 	}
-	showRow (cur + 1);
+	setFirstColumnSpanned (0, row, true);
+	setIndexWidget (model ()->index (0, 0, row), new RKWidgetGuard (0, default_widget, this));
 }
 
 void RKAccordionTable::setModel (QAbstractItemModel* model) {
 	RK_TRACE (MISC);
 
 	pmodel->setSourceModel (model);
-	QTableView::setModel (pmodel);
+	QTreeView::setModel (pmodel);
 
-	for (int i = 1; i < model->rowCount (); ++i) {
-		hideRow (i * 2 + 1);
-	}
-	if (model->rowCount () > 0) showRow (1);
+	if (pmodel->rowCount () > 0) expand (pmodel->index (0, 0));
 }
 
 void RKAccordionTable::resizeEvent (QResizeEvent* event) {
 	// TODO
-	QAbstractItemView::resizeEvent (event);
+	QTreeView::resizeEvent (event);
 }
 
-
 // TODO
 // - add buttons to each row
-// - handle row removals
 // - handle resize
-
 // KF5 TODO: remove:
 #include "rkaccordiontable.moc"
+#include "rkaccordiontablemodel_moc.cpp"
diff --git a/rkward/misc/rkaccordiontable.h b/rkward/misc/rkaccordiontable.h
index 5fbd716..7fc5530 100644
--- a/rkward/misc/rkaccordiontable.h
+++ b/rkward/misc/rkaccordiontable.h
@@ -19,12 +19,12 @@
 #define RKACCORDIONTABLE_H
 
 #include <QWidget>
-#include <QTableView>
-#include <QAbstractProxyModel>
+#include <QTreeView>
 
 class QAbstractItemModel;
+class QAbstractProxyModel;
 
-class RKAccordionTable : public QTableView {
+class RKAccordionTable : public QTreeView {
 	Q_OBJECT
 public:
 	RKAccordionTable (QWidget *parent);
@@ -33,10 +33,11 @@ public:
 	QWidget *defaultWidget () const { return default_widget; };
 
 	void setModel (QAbstractItemModel *model);
+public slots:
+	void rowExpanded (QModelIndex row);
 protected:
 	void currentChanged (const QModelIndex& current, const QModelIndex& previous);   // reimplemented to adjust selection / activate correct row
 	void resizeEvent (QResizeEvent* event);                                          // reimplemented to make the current content widget stretch / shrink
-	void rowsAboutToBeRemoved (const QModelIndex& parent, int start, int end);       // reimplemented to switch to a different row, if current row is about to be removed
 private:
 	QWidget *default_widget;
 	QAbstractProxyModel *pmodel;
diff --git a/rkward/plugin/rkoptionset.cpp b/rkward/plugin/rkoptionset.cpp
index 5f8b06c..6b12b95 100644
--- a/rkward/plugin/rkoptionset.cpp
+++ b/rkward/plugin/rkoptionset.cpp
@@ -176,6 +176,8 @@ RKOptionSet::RKOptionSet (const QDomElement &element, RKComponent *parent_compon
 		if (display_show_index) display->resizeColumnToContents (0);
 		else display->setColumnHidden (0, true);
 		display->setModel (model);
+		if (exp_mode == Detached) display->setItemDelegate (new RKOptionSetDelegate (this));
+		if (exp_mode == Accordion) accordion->setModel (model);
 		display->setSelectionBehavior (QAbstractItemView::SelectRows);
 		display->setSelectionMode (QAbstractItemView::SingleSelection);
 		connect (display->selectionModel (), SIGNAL (selectionChanged(QItemSelection,QItemSelection)), this, SLOT (currentRowChanged()));
@@ -215,8 +217,6 @@ RKComponent *RKOptionSet::createDisplay (bool show_index, QWidget *parent) {
 		display = new QTreeView (box);
 		display_show_index = show_index;
 		model = new RKOptionSetDisplayModel (this);
-		if (exp_mode == Detached) display->setItemDelegate (new RKOptionSetDelegate (this));
-		if (exp_mode == Accordion) accordion->setModel (model);
 	}
 
 	display_buttons = new KHBox (dummy);
diff --git a/rkward/plugins/testing/optionset.xml b/rkward/plugins/testing/optionset.xml
index 611eda0..f897f40 100644
--- a/rkward/plugins/testing/optionset.xml
+++ b/rkward/plugins/testing/optionset.xml
@@ -19,7 +19,7 @@
 		</text>
 		<tabbook>
 			<tab label="Manual set">
-				<optionset id="mset">
+				<optionset id="mset" exp_mode="accordion">
 					<content>
 						<frame>
 							<optiondisplay index="true"/>



More information about the rkward-tracker mailing list