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
+48 -8
View File
@@ -36,6 +36,7 @@
#include <QHash>
#include <QModelIndex>
#include <QStandardItem>
#include <QTimer>
#include <QUndoCommand>
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<DynamicTextItemDelegate *>(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<QColor>(), 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<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:
{
@@ -1788,15 +1828,15 @@ void DynamicTextItemDelegate::setModelData(
{
if(QStandardItem *qsi = qsim->itemFromIndex(index))
{
QColorDialog *cd = static_cast<QColorDialog *> (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<QColor>();
qsi->setData(color, Qt::EditRole);
qsi->setData(color, Qt::ForegroundRole);
}
return;
}
}
}
else if (editor->objectName() == "info_text")
+15
View File
@@ -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