Search code examples
c++qtqstyleditemdelegate

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.


Solution

  • 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 come 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.