Here is a method of capturing mouse events and wheel events based on the previous answer. Unfortunately, this method requires us to access protected members of the QWidget. I don't know how legal it is, but it works:
Constructor:
QmlPlainTextEdit::QmlPlainTextEdit(QQuickItem *parent)
: QQuickPaintedItem(parent)
{
// Set item flags
setFlag(ItemHasContents, true);
setFlag(ItemAcceptsInputMethod, true);
setFlag(ItemIsFocusScope, true);
setAcceptedMouseButtons(Qt::AllButtons);
// Initialize the text edit widget
m_textEdit = new QPlainTextEdit();
m_textEdit->installEventFilter(this);
m_textEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
// Set the QML item's implicit size
auto hint = m_textEdit->sizeHint();
setImplicitSize(hint.width(), hint.height());
// Resize QPlainTextEdit to fit QML item
connect(this, &QQuickPaintedItem::widthChanged, this,
&QmlPlainTextEdit::updateWidgetSize);
connect(this, &QQuickPaintedItem::heightChanged, this,
&QmlPlainTextEdit::updateWidgetSize);
}
Events:
bool QmlPlainTextEdit::event(QEvent *event)
{
switch (event->type())
{
case QEvent::FocusIn:
forceActiveFocus();
return QQuickPaintedItem::event(event);
break;
case QEvent::Wheel:
processWheelEvents(static_cast<QWheelEvent*>(event));
return true;
break;
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseButtonDblClick:
case QEvent::MouseMove:
processMouseEvents(static_cast<QMouseEvent*>(event));
return true;
break;
default:
break;
}
return QApplication::sendEvent(m_textEdit, event);
}
Event filter:
bool QmlPlainTextEdit::eventFilter(QObject *watched, QEvent *event)
{
Q_ASSERT(m_textEdit);
if (watched == m_textEdit)
{
switch (event->type())
{
case QEvent::Paint:
case QEvent::UpdateRequest:
update();
break;
default:
break;
}
}
return QQuickPaintedItem::eventFilter(watched, event);
}
Paint function:
void QmlPlainTextEdit::paint(QPainter *painter)
{
if (m_textEdit && painter)
m_textEdit->render(painter);
}
Process mouse/wheel events:
Doing some tests, I found out that QPlainTextEdit::event() returned false for mouse and wheel events. The quick and dirty solution was to call the appropriate event handlers directly. However, these functions are protected, so we need to do some unorthodox things in order to call these functions:
void QmlPlainTextEdit::processMouseEvents(QMouseEvent* event)
{
class Hack : public QPlainTextEdit{
public:
using QPlainTextEdit::mousePressEvent;
using QPlainTextEdit::mouseMoveEvent;
using QPlainTextEdit::mouseReleaseEvent;
using QPlainTextEdit::mouseDoubleClickEvent;
};
auto hack = static_cast<Hack*>(m_textEdit);
switch(event->type()) {
case QEvent::MouseButtonPress:
hack->mousePressEvent(event);
break;
case QEvent::MouseMove:
hack->mouseMoveEvent(event);
break;
case QEvent::MouseButtonRelease:
hack->mouseReleaseEvent(event);
break;
case QEvent::MouseButtonDblClick:
hack->mouseDoubleClickEvent(event);
break;
default:
break;
}
}
void QmlPlainTextEdit::processWheelEvents(QWheelEvent* event)
{
class Hack : public QPlainTextEdit{
public:
using QPlainTextEdit::wheelEvent;
};
static_cast<Hack*>(m_textEdit)->wheelEvent(event);
}
Resize widget to fit QML item:
void QmlPlainTextEdit::updateWidgetSize()
{
m_textEdit->setGeometry(0, 0, static_cast<int>(width()), static_cast<int>(height()));
update();
}
To use this class in QML, add in main.cpp:
qmlRegisterType<QmlPlainTextEdit>("QtWidgets", 1, 0, "QmlPlainTextEdit");
And finally, the QML item would be used like:
QmlPlainTextEdit {
id: textEdit
focus: true
Layout.fillWidth: true
Layout.fillHeight: true
onFocusChanged: {
textEdit.forceActiveFocus()
}
}
If anybody has a better way to process mouse/wheel events for this scenario, please post it here. I was stuck with this issue for three days and this is the only solution that I found.