Merge pull request #693 from ispyisail/fix/color-editor-crash-bug323

Fix crash changing dynamic text color and confirming with Enter (bugtracker #323)

Works like charm: 
- color is updated immediately
- no additional errors or warnings 
- no crash anymore!
This commit is contained in:
plc-user
2026-08-10 21:24:04 +02:00
committed by GitHub
2 changed files with 63 additions and 8 deletions
+47 -7
View File
@@ -36,6 +36,7 @@
#include <QHash> #include <QHash>
#include <QModelIndex> #include <QModelIndex>
#include <QStandardItem> #include <QStandardItem>
#include <QTimer>
#include <QUndoCommand> #include <QUndoCommand>
static int src_txt_row = 0; static int src_txt_row = 0;
@@ -1595,6 +1596,15 @@ DynamicTextItemDelegate::DynamicTextItemDelegate(QObject *parent) :
QStyledItemDelegate(parent) QStyledItemDelegate(parent)
{} {}
void DynamicTextItemDelegate::commitAndCloseDeferred(QWidget *editor) const
{
auto *self = const_cast<DynamicTextItemDelegate *>(this);
QTimer::singleShot(0, self, [self, editor]() {
emit self->commitData(editor);
emit self->closeEditor(editor);
});
}
QWidget *DynamicTextItemDelegate::createEditor( QWidget *DynamicTextItemDelegate::createEditor(
QWidget *parent, QWidget *parent,
const QStyleOptionViewItem &option, const QStyleOptionViewItem &option,
@@ -1682,13 +1692,43 @@ QWidget *DynamicTextItemDelegate::createEditor(
w->setProperty("ok", ok); w->setProperty("ok", ok);
} }
w->setObjectName("font_dialog"); w->setObjectName("font_dialog");
commitAndCloseDeferred(w);
return w; return w;
} }
case DynamicElementTextModel::color: case DynamicElementTextModel::color:
{ {
QColorDialog *cd = new QColorDialog(index.data(Qt::EditRole).value<QColor>(), parent); /* Like the font case above: run the dialog synchronously via
cd->setObjectName("color_dialog"); * its static convenience function and stash the result on a
return cd; * 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<QColor>(), 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: case DynamicElementTextModel::pos:
{ {
@@ -1788,11 +1828,11 @@ void DynamicTextItemDelegate::setModelData(
{ {
if(QStandardItem *qsi = qsim->itemFromIndex(index)) if(QStandardItem *qsi = qsim->itemFromIndex(index))
{ {
QColorDialog *cd = static_cast<QColorDialog *> (editor); if (editor->property("ok").toBool() == true)
if (cd->result() == QDialog::Accepted)
{ {
qsi->setData(cd->selectedColor(), Qt::EditRole); QColor color = editor->property("color").value<QColor>();
qsi->setData(cd->selectedColor(), Qt::ForegroundRole); qsi->setData(color, Qt::EditRole);
qsi->setData(color, Qt::ForegroundRole);
} }
return; return;
} }
+15
View File
@@ -155,6 +155,21 @@ class DynamicTextItemDelegate : public QStyledItemDelegate
private: private:
QStringList availableInfo(DynamicElementTextItem *deti) const; 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 #endif // DYNAMICELEMENTTEXTMODEL_H