diff --git a/sources/ui/dynamicelementtextmodel.cpp b/sources/ui/dynamicelementtextmodel.cpp index f33128729..6ad12570a 100644 --- a/sources/ui/dynamicelementtextmodel.cpp +++ b/sources/ui/dynamicelementtextmodel.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include static int src_txt_row = 0; @@ -1595,6 +1596,15 @@ DynamicTextItemDelegate::DynamicTextItemDelegate(QObject *parent) : QStyledItemDelegate(parent) {} +void DynamicTextItemDelegate::commitAndCloseDeferred(QWidget *editor) const +{ + auto *self = const_cast(this); + QTimer::singleShot(0, self, [self, editor]() { + emit self->commitData(editor); + emit self->closeEditor(editor); + }); +} + QWidget *DynamicTextItemDelegate::createEditor( QWidget *parent, const QStyleOptionViewItem &option, @@ -1682,13 +1692,43 @@ QWidget *DynamicTextItemDelegate::createEditor( w->setProperty("ok", ok); } w->setObjectName("font_dialog"); + commitAndCloseDeferred(w); return w; } case DynamicElementTextModel::color: { - QColorDialog *cd = new QColorDialog(index.data(Qt::EditRole).value(), parent); - cd->setObjectName("color_dialog"); - return cd; + /* Like the font case above: run the dialog synchronously via + * its static convenience function and stash the result on a + * plain placeholder widget, rather than handing back the + * QColorDialog itself as the item view's "editor". + * + * The item view's own Enter/Escape handling (the base + * QStyledItemDelegate::eventFilter(), since "color_dialog" + * isn't one of the objectNames special-cased in this + * delegate's own eventFilter() above) treats whatever + * createEditor() returned as a small inline editor it + * commits and destroys directly on a key press. A QColorDialog + * is not that: on Windows it can hand off to the native + * color picker, whose own accept/close path then races the + * view's -- pressing Enter fired both, one tearing down an + * object the other was still using (case #323 on the bug + * tracker: "QObject::installEventFilter(): Cannot filter + * events for objects in a different thread" immediately + * followed by a segfault; clicking the dialog's own OK + * button with the mouse didn't reach the view's key + * handling, so it didn't crash). Resolving the dialog + * before returning removes the second, competing teardown + * path entirely. */ + QColor color = QColorDialog::getColor(index.data(Qt::EditRole).value(), parent); + QWidget *w = new QWidget(parent); + if (color.isValid()) + { + w->setProperty("color", color); + w->setProperty("ok", true); + } + w->setObjectName("color_dialog"); + commitAndCloseDeferred(w); + return w; } case DynamicElementTextModel::pos: { @@ -1788,15 +1828,15 @@ void DynamicTextItemDelegate::setModelData( { if(QStandardItem *qsi = qsim->itemFromIndex(index)) { - QColorDialog *cd = static_cast (editor); - if (cd->result() == QDialog::Accepted) + if (editor->property("ok").toBool() == true) { - qsi->setData(cd->selectedColor(), Qt::EditRole); - qsi->setData(cd->selectedColor(), Qt::ForegroundRole); + QColor color = editor->property("color").value(); + qsi->setData(color, Qt::EditRole); + qsi->setData(color, Qt::ForegroundRole); } return; } - + } } else if (editor->objectName() == "info_text") diff --git a/sources/ui/dynamicelementtextmodel.h b/sources/ui/dynamicelementtextmodel.h index 67b42e23e..013d9318b 100644 --- a/sources/ui/dynamicelementtextmodel.h +++ b/sources/ui/dynamicelementtextmodel.h @@ -155,6 +155,21 @@ class DynamicTextItemDelegate : public QStyledItemDelegate private: QStringList availableInfo(DynamicElementTextItem *deti) const; + /** + @brief commitAndCloseDeferred + Schedule commitData()/closeEditor() for @a editor on the next + event loop iteration. For editors resolved synchronously inside + createEditor() (font/color, both run their picker dialog before + returning) there is no user interaction left to drive the base + QStyledItemDelegate::eventFilter()'s usual Enter/focus-out commit + path, so without this the value sits picked-but-uncommitted + until something unrelated (e.g. clicking elsewhere) happens to + trigger it. Deferred rather than called immediately: the view + only registers the widget createEditor() returns as "the active + editor" *after* createEditor() itself returns, so emitting here + would target an editor the view doesn't know about yet. + */ + void commitAndCloseDeferred(QWidget *editor) const; }; #endif // DYNAMICELEMENTTEXTMODEL_H