CSLover CSLover - 2 months ago 29
C++ Question

QStyledItemDelegate partially select text of default QLineEdit editor

I have a subclass of

QStyledItemDelegate
which at the moment does not reimplement any functions (for simplicity of the question).

With default
QStyledItemDelegate
implementation, when the user begins to edit text in a
QTableView
, the delegate draws a
QLineEdit
with the text from the model, and selects all of it (highlights all for editing).

The text represents file names such as "document.pdf". The user is allowed to edit this entire text, however, I only want to initially highlight the base name portion ("document") and not the suffix ("pdf"). How can I do this? (I don't need the logic of how to do this, I need to know how to get the
QStyledItemDelegate
to highlight a portion of text)

I've tried:



Please help, and thanks in advance. A code snippet with say selecting the first 3 characters of text would be greatly appreciated.

Answer

As noted in my comments to the question, the problem with subclassing QStyledItemDelegate and trying to set any default selection in setEditorData like this:

void setEditorData(QWidget* editor, const QModelIndex &index)const{
    QStyledItemDelegate::setEditorData(editor, index);
    if(index.column() == 0){ //the column with file names in it
        //try to cast the default editor to QLineEdit
        QLineEdit* le= qobject_cast<QLineEdit*>(editor);
        if(le){
            //set default selection in the line edit
            int lastDotIndex= le->text().lastIndexOf("."); 
            le->setSelection(0,lastDotIndex);
        }
    }
}

is that (in Qt code) after the view calls our setEditorData here, it tries to call selectAll() here when the editor widget is a QLineEdit. That means that whatever selection we provide in setEditorData will be changed afterwards.

The only solution I could came up with, was to provide our selection in a queued manner. So that, our selection is set when execution is back into the event loop. Here is working example:

screenshot

#include <QApplication>
#include <QtWidgets>

class FileNameDelegate : public QStyledItemDelegate{
public:
    explicit FileNameDelegate(QObject* parent= nullptr)
        :QStyledItemDelegate(parent){}
    ~FileNameDelegate(){}

    void setEditorData(QWidget* editor, const QModelIndex &index)const{
        QStyledItemDelegate::setEditorData(editor, index);
        //the column with file names in it
        if(index.column() == 0){
            //try to cast the default editor to QLineEdit
            QLineEdit* le= qobject_cast<QLineEdit*>(editor);
            if(le){
                QObject src;
                //the lambda function is executed using a queued connection
                connect(&src, &QObject::destroyed, le, [le](){
                    //set default selection in the line edit
                    int lastDotIndex= le->text().lastIndexOf(".");
                    le->setSelection(0,lastDotIndex);
                }, Qt::QueuedConnection);
            }
        }
    }
};

//Demo program

int main(int argc, char** argv){
    QApplication a(argc, argv);

    QStandardItemModel model;
    QList<QStandardItem*> row;
    QStandardItem item("document.pdf");
    row.append(&item);
    model.appendRow(row);
    FileNameDelegate delegate;
    QTableView tableView;
    tableView.setModel(&model);
    tableView.setItemDelegate(&delegate);
    tableView.show();

    return a.exec();
}

This may sound like a hack, but I decided to write this until someone has a better approach to the problem.

Comments