[rkward/work/optionset_experiments] rkward: More progress on the accordion table:

Thomas Friedrichsmeier thomas.friedrichsmeier at ruhr-uni-bochum.de
Tue Oct 27 09:36:46 UTC 2015


Git commit 80047a0dba538e93a6caee235d50be74e178f5a9 by Thomas Friedrichsmeier.
Committed on 27/10/2015 at 09:35.
Pushed by tfry into branch 'work/optionset_experiments'.

More progress on the accordion table:
- Sane intial size and resizing.
- Add-/Remove buttons connected

M  +112  -17   rkward/misc/rkaccordiontable.cpp
M  +11   -3    rkward/misc/rkaccordiontable.h
M  +28   -8    rkward/plugin/rkoptionset.cpp
M  +2    -0    rkward/plugin/rkoptionset.h

http://commits.kde.org/rkward/80047a0dba538e93a6caee235d50be74e178f5a9

diff --git a/rkward/misc/rkaccordiontable.cpp b/rkward/misc/rkaccordiontable.cpp
index 3506fb4..7383919 100644
--- a/rkward/misc/rkaccordiontable.cpp
+++ b/rkward/misc/rkaccordiontable.cpp
@@ -140,6 +140,7 @@ public:
 		RKWidgetGuard::fallback_parent = fallback_parent;
 
 		QVBoxLayout *layout = new QVBoxLayout (this);
+		layout->setContentsMargins (0, 0, 0, 0);
 		guarded->setParent (this);
 		layout->addWidget (guarded);
 	}
@@ -161,7 +162,15 @@ RKAccordionTable::RKAccordionTable (QWidget* parent) : QTreeView (parent) {
 	RK_TRACE (MISC);
 
 	show_add_remove_buttons = false;
-	default_widget = new KVBox;
+
+	// This may seem like excessive wrapping. The point is to be able to manipulate the editor_widget_container's sizeHint(), while
+	// keeping the editor_widget's sizeHint() intact.
+	editor_widget_container = new QWidget ();
+	QHBoxLayout *layout = new QHBoxLayout (editor_widget_container);
+	layout->setContentsMargins (0, 0, 0, 0);
+	editor_widget = new KVBox (editor_widget_container);
+	layout->addWidget (editor_widget);
+
 	setSelectionMode (NoSelection);
 	setIndentation (0);
 	setExpandsOnDoubleClick (false);   // we expand on single click, instead
@@ -175,18 +184,59 @@ RKAccordionTable::RKAccordionTable (QWidget* parent) : QTreeView (parent) {
 RKAccordionTable::~RKAccordionTable () {
 	RK_TRACE (MISC);
 
-	delete default_widget;
+	delete editor_widget;
 }
 
-QSize RKAccordionTable::minimumSizeHint () const {
+QSize RKAccordionTable::sizeHintWithoutEditor () const {
 	RK_TRACE (MISC);
 
-	QSize min = default_widget->minimumSize ();
-	min.setHeight (min.height () + horizontalScrollBar ()->minimumSizeHint ().height () + sizeHintForRow (0) * 4);
-	min.setWidth (qMax (min.width (), QTreeView::minimumSizeHint ().width ()));
+	return (QSize (minimumSizeHint ().width (), horizontalScrollBar ()->minimumSizeHint ().height () + sizeHintForRow (0) * 4));
+}
+
+QSize RKAccordionTable::sizeHint () const {
+	RK_TRACE (MISC);
+
+	QSize swoe = sizeHintWithoutEditor ();
+	QSize min = editor_widget->sizeHint ();
+	min.setHeight (min.height () + swoe.height ());
+	min.setWidth (qMax (min.width (), swoe.width ()));
 	return min;
 }
 
+void RKAccordionTable::resizeEvent (QResizeEvent* event) {
+	RK_TRACE (MISC);
+
+	QSize esh = editor_widget->sizeHint ();
+	int available_height = height () - sizeHintWithoutEditor ().height ();
+	int extra_height = available_height - esh.height ();
+	editor_widget_container->setMinimumHeight (esh.height () + qMax (0, 2 * (extra_height / 3)));
+	editor_widget_container->setMaximumWidth (width ());
+
+	QTreeView::resizeEvent (event);
+
+	// NOTE: For Qt 4.8.6, an expanded editor row will _not_ be updated, automatically.
+	// We have to force this by hiding / unhiding it.
+	QModelIndex expanded;
+	for (int i = 0; i < model ()->rowCount (); ++i) {
+		if (isExpanded (model ()->index (i, 0))) {
+			expanded = model ()->index (i, 0);
+			break;
+		}
+	}
+	if (expanded.isValid ()) {
+		setUpdatesEnabled (false);
+		setRowHidden (0, expanded, true);
+		setRowHidden (0, expanded, false);
+		setUpdatesEnabled (true);
+	}
+}
+
+void RKAccordionTable::activateRow (int row) {
+	RK_TRACE (MISC);
+
+	setExpanded (model ()->index (row, 0), true);
+}
+
 void RKAccordionTable::rowClicked (QModelIndex row) {
 	RK_TRACE (MISC);
 
@@ -206,8 +256,10 @@ void RKAccordionTable::rowExpanded (QModelIndex row) {
 		}
 	}
 	setFirstColumnSpanned (0, row, true);
-	setIndexWidget (model ()->index (0, 0, row), new RKWidgetGuard (0, default_widget, this));
+	setIndexWidget (model ()->index (0, 0, row), new RKWidgetGuard (0, editor_widget_container, this));
 	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 ()));
 }
 
@@ -227,14 +279,19 @@ void RKAccordionTable::updateWidget () {
 			QHBoxLayout *layout = new QHBoxLayout (display_buttons);
 			layout->setContentsMargins (0, 0, 0, 0);
 			layout->setSpacing (0);
+
 			QToolButton *add_button = new QToolButton (display_buttons);
+			connect (add_button, SIGNAL (clicked(bool)), this, SLOT (addClicked()));
 			add_button->setIcon (RKStandardIcons::getIcon (RKStandardIcons::ActionInsertRow));
 			RKCommonFunctions::setTips (i18n ("Add a row / element"), add_button);
+			layout->addWidget (add_button);
+
 			QToolButton *remove_button = new QToolButton (display_buttons);
+			connect (remove_button, SIGNAL (clicked(bool)), this, SLOT (removeClicked()));
 			remove_button->setIcon (RKStandardIcons::getIcon (RKStandardIcons::ActionDeleteRow));
 			RKCommonFunctions::setTips (i18n ("Remove a row / element"), remove_button);
-			layout->addWidget (add_button);
 			layout->addWidget (remove_button);
+
 			setIndexWidget (row, display_buttons);
 		}
 	}
@@ -242,6 +299,46 @@ void RKAccordionTable::updateWidget () {
 	if (show_add_remove_buttons) header ()->setResizeMode (0, QHeaderView::ResizeToContents);
 }
 
+int RKAccordionTable::rowOfButton (QObject* button) const {
+	RK_TRACE (MISC);
+
+	if (!button) return -1;
+
+	// we rely on the fact that the buttons in use, here, are encapsulaped in a parent widget, which is set as indexWidget()
+	QObject* button_parent = button->parent ();
+	for (int i = model ()->rowCount () - 1; i >= 0; --i) {
+		QModelIndex row = model ()->index (i, 0);
+		if (button_parent == indexWidget (row)) {
+			return i;
+		}
+	}
+	RK_ASSERT (false);
+	return -1;
+}
+
+
+void RKAccordionTable::addClicked () {
+	RK_TRACE (MISC);
+
+	int row = rowOfButton (sender ());
+	if (row < 0) {
+		RK_ASSERT (row >= 0);
+		return;
+	}
+	emit (addRow (row));
+}
+
+void RKAccordionTable::removeClicked () {
+	RK_TRACE (MISC);
+
+	int row = rowOfButton (sender ());
+	if (row < 0) {
+		RK_ASSERT (row >= 0);
+		return;
+	}
+	emit (removeRow (row));
+}
+
 void RKAccordionTable::setModel (QAbstractItemModel* model) {
 	RK_TRACE (MISC);
 
@@ -252,19 +349,17 @@ void RKAccordionTable::setModel (QAbstractItemModel* model) {
 	connect (pmodel, SIGNAL (rowsRemoved(const QModelIndex&,int,int)), this, SLOT (updateWidget()));
 
 	if (pmodel->rowCount () > 0) expand (pmodel->index (0, 0));
-}
 
-void RKAccordionTable::resizeEvent (QResizeEvent* event) {
-	// TODO
-	QTreeView::resizeEvent (event);
+	updateGeometry ();   // TODO: Not so clean to call this, here. But at this point we know the display_widget has been constructed, too
 }
 
+
 // TODO
-// - add buttons to each row (in correct size)
-// - handle resize
-// - fix initial size
-// - margins
-// - handle row insertions / removals correctly
+// - insertion when now row is available, yet
+// - expand / collapse indicator?
+// - drag-reordering?
+//   - will this make per-item add buttons obsolete?
+
 // KF5 TODO: remove:
 #include "rkaccordiontable.moc"
 #include "rkaccordiontablemodel_moc.cpp"
diff --git a/rkward/misc/rkaccordiontable.h b/rkward/misc/rkaccordiontable.h
index 5e68f54..b4af29c 100644
--- a/rkward/misc/rkaccordiontable.h
+++ b/rkward/misc/rkaccordiontable.h
@@ -30,25 +30,33 @@ public:
 	RKAccordionTable (QWidget *parent);
 	~RKAccordionTable ();
 
-	QWidget *defaultWidget () const { return default_widget; };
+	QWidget *editorWidget () const { return editor_widget; };
 
 	void setModel (QAbstractItemModel *model);
 	void setShowAddRemoveButtons (bool show) {
 		show_add_remove_buttons = show;
 	}
 
-	QSize minimumSizeHint () const;                                                  // reimplemented to assure a proper size for the content
+	QSize sizeHint () const;                                                  // reimplemented to assure a proper size for the content
 public slots:
 	void rowExpanded (QModelIndex row);
 	void rowClicked (QModelIndex row);
 	void updateWidget ();
+	void addClicked ();
+	void removeClicked ();
+	void activateRow (int row);
 signals:
 	void activated (int row);
+	void addRow (int where);
+	void removeRow (int which);
 protected:
 	void resizeEvent (QResizeEvent* event);                                          // reimplemented to make the current content widget stretch / shrink
 private:
+	QSize sizeHintWithoutEditor () const;
+	int rowOfButton (QObject *button) const;
 	bool show_add_remove_buttons;
-	QWidget *default_widget;
+	QWidget *editor_widget;
+	QWidget *editor_widget_container;
 	QAbstractProxyModel *pmodel;
 };
 
diff --git a/rkward/plugin/rkoptionset.cpp b/rkward/plugin/rkoptionset.cpp
index 13d4737..00fa92e 100644
--- a/rkward/plugin/rkoptionset.cpp
+++ b/rkward/plugin/rkoptionset.cpp
@@ -58,7 +58,9 @@ RKOptionSet::RKOptionSet (const QDomElement &element, RKComponent *parent_compon
 	switcher->addWidget (user_area);
 	if (exp_mode == Accordion) {
 		accordion = new RKAccordionTable (user_area);
-		connect (accordion, SIGNAL(activated(int)), this, SLOT(currentRowChanged(int)));
+		connect (accordion, SIGNAL (activated(int)), this, SLOT(currentRowChanged(int)));
+		connect (accordion, SIGNAL (addRow(int)), this, SLOT(addRow(int)));
+		connect (accordion, SIGNAL (removeRow(int)), this, SLOT(removeRow(int)));
 	}
 	updating_notice = new QLabel (i18n ("Updating status, please wait"), this);
 	switcher->addWidget (updating_notice);
@@ -83,10 +85,10 @@ RKOptionSet::RKOptionSet (const QDomElement &element, RKComponent *parent_compon
 	// first build the contents, as we will need to refer to the elements inside, later
 	model = 0;
 	display = 0;	// will be created from the builder, on demand -> createDisplay ()
-	contents_container = new RKComponent (this, exp_mode == RKOptionSet::Accordion ? accordion->defaultWidget () : user_area);
+	contents_container = new RKComponent (this, exp_mode == RKOptionSet::Accordion ? accordion->editorWidget () : user_area);
 	QDomElement content_element = xml->getChildElement (element, "content", DL_ERROR);
 	RKComponentBuilder *builder = new RKComponentBuilder (contents_container, content_element);
-	builder->buildElement (content_element, *xml, exp_mode == Accordion ? accordion->defaultWidget () : user_area, false);	// NOTE that parent widget != parent component, here, by intention. The point is that the display should not be disabled along with the contents
+	builder->buildElement (content_element, *xml, exp_mode == Accordion ? accordion->editorWidget () : user_area, false);	// NOTE that parent widget != parent component, here, by intention. The point is that the display should not be disabled along with the contents
 	builder->parseLogic (xml->getChildElement (element, "logic", DL_INFO), *xml, false);
 	builder->makeConnections ();
 	addChild ("contents", contents_container);
@@ -180,7 +182,6 @@ RKOptionSet::RKOptionSet (const QDomElement &element, RKComponent *parent_compon
 		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()));
@@ -192,6 +193,10 @@ RKOptionSet::RKOptionSet (const QDomElement &element, RKComponent *parent_compon
 		}
 	}
 	if (!keycolumn && (exp_mode == Accordion)) accordion->setShowAddRemoveButtons (true);
+	if (exp_mode == Accordion) {
+		accordion->setModel (model);
+		if (display) display->hide ();
+	}
 }
 
 RKOptionSet::~RKOptionSet () {
@@ -202,7 +207,7 @@ void RKOptionSet::fetchDefaults () {
 	RK_TRACE (PLUGIN);
 	RK_ASSERT (default_row_state.isEmpty ());
 	contents_container->fetchPropertyValuesRecursive (&default_row_state, false, QString (), true);
-	if (min_rows && !keycolumn && (rowCount () <= 0)) addRow ();
+	if (min_rows && !keycolumn && (rowCount () <= 0)) addRow (rowCount ());
 	contents_container->enablednessProperty ()->setBoolValue (rowCount () > 0);	// no current row; Do this *after* fetching default values, however. Otherwise most values will *not* be read, as the element is disabled
 }
 
@@ -416,14 +421,19 @@ void RKOptionSet::updateUnfinishedRows () {
 	RK_ASSERT (false);	// This would mean, we did not find any unfinished row, even though we tested for n_unfinished_rows, above.
 }
 
+// TODO: removeMe
 void RKOptionSet::addRow () {
 	RK_TRACE (PLUGIN);
+	addRow (active_row >= 0 ? active_row + 1 : rowCount ());	// append feels more natural than insert, here
+}
+
+void RKOptionSet::addRow (int row) {
+	RK_TRACE (PLUGIN);
 
 	storeRowSerialization (active_row);
 
-	int row = active_row + 1;	// append feels more natural than insert, here
 	int nrows = rowCount ();
-	if (row <= 0) row = nrows;
+	if (row < 0) row = nrows;
 	RK_ASSERT (!keycolumn);
 
 	if (display) model->beginInsertRows (QModelIndex (), row, row);
@@ -455,10 +465,15 @@ void RKOptionSet::addRow () {
 	changed ();
 }
 
+// TODO: removeMe
 void RKOptionSet::removeRow () {
 	RK_TRACE (PLUGIN);
+	removeRow (active_row);
+}
+
+void RKOptionSet::removeRow (int row) {
+	RK_TRACE (PLUGIN);
 
-	int row = active_row;
 	int nrows = rowCount ();
 	if (row < 0) {
 		RK_ASSERT (false);
@@ -757,6 +772,11 @@ void RKOptionSet::updateCurrentRowInDisplay () {
 	else {
 		display->selectionModel ()->select (display->model ()->index (active_row, 0), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
 	}
+
+	if (exp_mode == Accordion) {
+		if (active_row < 0) accordion->collapseAll ();
+		else accordion->activateRow (active_row);
+	}
 }
 
 void RKOptionSet::currentRowChanged () {
diff --git a/rkward/plugin/rkoptionset.h b/rkward/plugin/rkoptionset.h
index 095190f..ad52247 100644
--- a/rkward/plugin/rkoptionset.h
+++ b/rkward/plugin/rkoptionset.h
@@ -52,7 +52,9 @@ private slots:
 	void columnPropertyChanged (RKComponentPropertyBase *property);
 	void currentRowPropertyChanged (RKComponentPropertyBase *property);
 	void serializationPropertyChanged (RKComponentPropertyBase *property);
+	void addRow (int where);
 	void addRow ();
+	void removeRow (int which);
 	void removeRow ();
 	void currentRowChanged ();
 	void currentRowChanged (int row);



More information about the rkward-tracker mailing list