[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