qtcontextmenuqstyleditemdelegateqplaintexteditqevent

QContextMenu issue with QPlainTextEdit item delegat in QTableView


I have a QTableView with delegates for certain columns. The default editor for indices is QLineEdit, for the 'comment' column delegate however I chose a QPlainTextEdit. Getting that to expand and adjust to contents was a PITA on its own (see https://stackoverflow.com/a/76713127/9540775), but it works satisfactorily now, i.e. the editor expands as desired beyond the rectangle of the table index.

Though, while left clicks work as expected no matter the position (inside the editor), I noticed that right-clicks are not triggering the context menu if the click position is outside the rectangle of the underlying table index. The position is definitely registered as inside the editor widget, the viewport and the QFrame, still it is handled like 'outside' and causes the editor to close.

I tried catching the clicks with a keypressEvent() override and tried to debug via a function connected to setContextMenuPolicy(Qt::CustomContextMenu);, but to avail. Clicks were registered as expected but I couldn't make the menu show up, the editor would always close.

multilineeditdelegate.h

#include <QPlainTextEdit>

// a QStyledItemDelegate subclass
#include "library/tableitemdelegate.h"

class MultiLineEditor : public QPlainTextEdit {
    Q_OBJECT
  public:
    MultiLineEditor(QWidget* pParent);

    bool eventFilter(QObject* obj, QEvent* event) override;

  signals:
    void editingFinished();
};

class MultiLineEditDelegate : public TableItemDelegate {
    Q_OBJECT
  public:
    explicit MultiLineEditDelegate(QTableView* pTrackTable);
    ~MultiLineEditDelegate() override = default;

    // called when the user starts editing an item
    QWidget* createEditor(QWidget* parent,
            const QStyleOptionViewItem& option,
            const QModelIndex& index) const override;

  private:
    void adjustEditor(MultiLineEditor* pEditor, const QSizeF size) const;
};

multilineeditdelegate.cpp

#include "multilineeditdelegate.h"

#include <QAbstractTextDocumentLayout>
#include <cmath>

#include "moc_multilineeditdelegate.cpp"

MultiLineEditor::MultiLineEditor(QWidget* pParent)
        : QPlainTextEdit(pParent) {
    setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
    setLineWrapMode(QPlainTextEdit::NoWrap);
    setContentsMargins(0, 0, 0, 0);
    document()->setDocumentMargin(0);
    installEventFilter(this);
};

bool MultiLineEditor::eventFilter(QObject* obj, QEvent* event) {
    if (event->type() == QEvent::MouseButtonPress) {
        QMouseEvent* me = static_cast<QMouseEvent*>(event);
        if (me->button() == Qt::RightButton && rect().contains(me->pos(), false)) {
            return true;
        }
    }
    return QPlainTextEdit::eventFilter(obj, event);
}

MultiLineEditDelegate::MultiLineEditDelegate(QTableView* pTableView)
        : TableItemDelegate(pTableView),
          m_lineCount(0) {
}

QWidget* MultiLineEditDelegate::createEditor(QWidget* pParent,
        const QStyleOptionViewItem& option,
        const QModelIndex& index) const {
    Q_UNUSED(index);
    auto* pEditor = new MultiLineEditor(pParent);
    auto* pDocLayout = pEditor->document()->documentLayout();
    connect(pDocLayout,
            &QAbstractTextDocumentLayout::documentSizeChanged,
            this,
            [this, pEditor](const QSizeF size) {
                adjustEditor(pEditor, size);
            });
}

void MultiLineEditDelegate::adjustEditor(MultiLineEditor* pEditor, const QSizeF size) const {
    // fancy calculations to expand editor to fit content
    // while limiting it to the rectangle of the parent QTableView
    pEditor->setGeometry(QRect(m_editRect.x(), newY, newW, newH));
}

Solution

  • While writing my question I remembered bool QWidget::eventFilter.
    Simply returning true for all right-clicks inside the editor (viewport + scrollbars) fixed the issue. I now get a context menu as desired.

    bool MultiLineEditor::eventFilter(QObject* obj, QEvent* event) {
        if (event->type() == QEvent::MouseButtonPress) {
            QMouseEvent* me = static_cast<QMouseEvent*>(event);
            if (me->button() == Qt::RightButton && rect().contains(me->pos(), false)) {
                return true;
            }
        }
        return QPlainTextEdit::eventFilter(obj, event);
    }