Commit font/color edits immediately instead of waiting for an unrelated click

plc-user on PR #693: the crash is fixed, but the color/font field and
the on-diagram text no longer update until you leave the properties
list and click in the diagram -- previously it updated as soon as you
clicked OK.

That's a side effect of the crash fix itself. The old, crashing code
returned a *live* QColorDialog as the item view's editor; clicking its
OK button called accept()/hide() on it, and hiding the active editor
happens to trip the base delegate's own focus-lost commit path -- so
the value applied immediately, racily, as a side effect of the same
mechanism that crashed on Enter. The fix (commit 4bd9b6b21) replaced
that with running the dialog synchronously inside createEditor() and
returning an inert placeholder with the result stashed in a property.
Correct for the crash, but it also removed that accidental commit
trigger: the placeholder never had focus to lose, so nothing tells
the view to read the value back until some unrelated interaction
(clicking away) incidentally triggers it.

Fix: explicitly emit commitData()/closeEditor() for the resolved
editor, deferred via QTimer::singleShot(0, ...) since the view only
registers createEditor()'s return value as "the active editor" after
createEditor() itself returns -- emitting synchronously, before
returning, would target a widget the view doesn't know about yet.
Applied to both font and color, since both share the exact same
"resolve synchronously in createEditor(), return an inert
placeholder" shape and thus the exact same gap; font just hadn't been
reported.

Verified with the same standalone harness from the crash fix (real
QTreeView + DynamicTextItemDelegate + QAbstractItemView::edit()),
this time deliberately *not* sending the synthetic Enter keypress the
crash-fix verification needed: clicks the dialog's real OK button,
lets the event loop run, and confirms the picked color lands in the
model on its own. Also reconfirmed the crash fix itself still holds
(clean exit, no synthetic-Enter needed either way now) and did a full
Release build (504/504) with no new warnings.
This commit is contained in:
ispyisail
2026-08-10 20:29:44 +12:00
parent 4bd9b6b212
commit b0b5345e15
2 changed files with 27 additions and 0 deletions
+12
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,6 +1692,7 @@ QWidget *DynamicTextItemDelegate::createEditor(
w->setProperty("ok", ok);
}
w->setObjectName("font_dialog");
commitAndCloseDeferred(w);
return w;
}
case DynamicElementTextModel::color:
@@ -1716,6 +1727,7 @@ QWidget *DynamicTextItemDelegate::createEditor(
w->setProperty("ok", true);
}
w->setObjectName("color_dialog");
commitAndCloseDeferred(w);
return w;
}
case DynamicElementTextModel::pos:
+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