Delegate + Multi-Line Text



  • I have a QTreeView, QStandardItemModel, and QStandardItems. The items that contain text I want to give the ability to be displayed over multiple lines within the treeview row it belongs to which is a word-wrapping effect. I'm not sure how to achieve this using a custom delegate, paint, and size hint functions. I also want it to key off the column size so when the treeview is expanded the 'word wrapping' effect will adjust as necessary. Any help would be appreciated. Below is the skeleton, but I'm not sure how to make use of the two key functions to get the desired results.

    wordwrapdelegate.h
    @
    #ifndef WORDWRAPITEMDELEGATE_H
    #define WORDWRAPITEMDELEGATE_H

    #include <QStyledItemDelegate>
    #include <QWidget>
    #include <QtGui>

    class WordWrapItemDelegate : public QStyledItemDelegate
    {
    Q_OBJECT

    public:
    WordWrapItemDelegate(QWidget *parent = 0) : QStyledItemDelegate(parent) {}

    void paint(QPainter *painter, const QStyleOptionViewItem &option,const QModelIndex &index) const;
    QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
    

    };

    #endif // WORDWRAPITEMDELEGATE_H
    @

    wordwrapdelegate.cpp
    @
    #include <QtGui>

    #include "wordwrapitemdelegate.h"

    void WordWrapItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
    {
    //Try and word wrap strings
    if(qVariantCanConvert<QString>(index.data()))
    {
    painter->save();

        QPalette::ColorGroup cg = option.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled;
        if (cg == QPalette::Normal && !(option.state & QStyle::State_Active))
            cg = QPalette::Inactive;
    
        //set pen color depending on behavior
        if (option.state & QStyle::State_Selected)
            painter->setPen(opt.palette.color(cg, QPalette::HighlightedText));
        else
            painter->setPen(opt.palette.color(cg, QPalette::Text));
    
        QString text = qvariant_cast<QString>(index.data());
        QFont font = QApplication::font();
    
        painter->setFont(font);
    
        //How to break text into multiple lines and re-size row accordingly?
    
        painter->restore();
    }
    else
    {
        //Fall back on original
        QStyledItemDelegate::paint(painter, option, index);
    }
    

    }

    QSize WordWrapItemDelegate::sizeHint(const QStyleOptionViewItem &option,const QModelIndex &index) const
    {
    //Try and word wrap strings
    if(qVariantCanConvert<QString>(index.data()))
    {
    //How to figure out size hint?
    return( QSize() );
    }
    else
    {
    //Fall back on original size hint of item
    return QStyledItemDelegate::sizeHint(option, index);
    }
    }
    @



  • I sort of figured it out, but have some issues.

    1)When rendered there are issues with the height of items (word-wrapped or not word-wrapped), the rectangle has padding at the top and bottom which I don't want
    2)Selecting an item makes it vanish and doesn't provide normal highlighting behavior
    3)Editing an item puts it back to a single line and doesn't provide word wrapping (which is fine, but I coded it to include word wrapping for presentational purposes and it's not working).

    I'm using a text layout to figure out word wrapping and I also want to provide the '...' truncating functionality b/c I don't want to allow extremely large entries of dozens of lines to be displayed, those I just defer to tool tips.

    Any help would be appreciated.

    wordwrapitemdelegate.h
    @
    #ifndef WORDWRAPITEMDELEGATE_H
    #define WORDWRAPITEMDELEGATE_H

    #include <QStyledItemDelegate>
    #include <QWidget>
    #include <QtGui>

    #define LINE_LIMIT 5 /Truncate with ... after a certain amount of word wrapped lines/

    class WordWrapItemDelegate : public QStyledItemDelegate
    {
    Q_OBJECT

    public:
    WordWrapItemDelegate(QWidget *parent = 0) : QStyledItemDelegate(parent) {}

    void paint(QPainter *painter, const QStyleOptionViewItem &option,const QModelIndex &index) const;
    QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
    void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &) const;
    

    private:
    void details( QString text, QFont font, const QStyleOptionViewItem &option, int *lineCount, int *widthUsed ) const;
    };

    #endif // WORDWRAPITEMDELEGATE_H
    @

    wordwrapitemdelegate.cpp
    @
    #include <QtGui>

    #include "wordwrapitemdelegate.h"

    void WordWrapItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
    {
    int widthUsed, lineCount;

    //Try and word wrap strings
    if(qVariantCanConvert<QString>(index.data()))
    {
        painter->save();
    
        QPalette::ColorGroup group = option.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled;
        if (group == QPalette::Normal && !(option.state & QStyle::State_Active))
            group = QPalette::Inactive;
    
        //set pen color depending on behavior
        painter->setFont( QApplication::font() );
        if (option.state & QStyle::State_Selected)
            painter->setPen(option.palette.color(group, QPalette::HighlightedText));
        else
            painter->setPen(option.palette.color(group, QPalette::Text));
    
        //Text from item
        QString text = index.data(Qt::DisplayRole).toString();
    
        //Begin word-wrapping effect (use the provided rectangles width to determine when to wrap)
        details(text, QApplication::font(), option, &lineCount, &widthUsed);
    
        //Word wrap the text, 'elide' it if it goes past a pre-determined maximum
        QString newText = painter->fontMetrics().elidedText(text, Qt::ElideRight, widthUsed);
        painter->drawText( option.rect, (Qt::TextWrapAnywhere|Qt::TextWordWrap|Qt::AlignTop|Qt::AlignLeft), newText );
        painter->restore();
    }
    else
    {
        //Fall back on original
        QStyledItemDelegate::paint(painter, option, index);
    }
    

    }

    QSize WordWrapItemDelegate::sizeHint(const QStyleOptionViewItem &option,const QModelIndex &index) const
    {
    int widthUsed, lineCount;

    //Try and word wrap strings
    if(qVariantCanConvert<QString>(index.data()))
    {
        //Update the size based on the number of lines (original size of a single line multiplied
        //by the number of lines)
        QString text = index.data(Qt::DisplayRole).toString();
        details(text, QApplication::font(), option, &lineCount, &widthUsed);
    
        QSize size = QStyledItemDelegate::sizeHint(option, index);
        size.setHeight(lineCount *  size.height());
    
        return size;
    }
    else
    {
        //Fall back on original size hint of item
        return QStyledItemDelegate::sizeHint(option, index);
    }
    

    }

    void WordWrapItemDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index ) const
    {
    int lineCount, widthUsed;

    //Fix editor's geometry and produce the word-wrapped effect for strings...
    if(qVariantCanConvert<QString>(index.data()))
    {
        //We need to expand our editor accordingly (not really necessary but looks nice)
        QString text = index.data(Qt::DisplayRole).toString();
        details(text, QApplication::font(), option, &lineCount, &widthUsed);
    
        //Expand
        QSize extraSize = QSize(option.rect.width(), lineCount * option.rect.height());
        QRect extraRect = option.rect;
        extraRect.setSize(extraSize);
        editor->setGeometry(option.rect);
    }
    else
    {
        //Fall back on original update
        QStyledItemDelegate::updateEditorGeometry(editor, option, index);
    }
    

    }

    void WordWrapItemDelegate::details( QString text, const QFont font, const QStyleOptionViewItem &option, int *lineCount, int *widthUsed ) const
    {
    //Use text layout to word-wrap and provide informmation about line counts and width's
    QTextLayout textLayout(text);

    *widthUsed = 0;
    *lineCount = 0;
    textLayout.setFont(font);
    textLayout.beginLayout();
    while (*lineCount < LINE_LIMIT)
    {
        *lineCount = *lineCount + 1;
        QTextLine line = textLayout.createLine();
        if (!line.isValid())
            break;
    
        line.setLineWidth(option.rect.width());
        *widthUsed = (*widthUsed + line.naturalTextWidth());
    }
    textLayout.endLayout();
    *widthUsed = (*widthUsed + option.rect.width());
    

    }
    @


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.