PDA

View Full Version : Subclassing QPushButton to support word wrapping



Berryblue031
9th June 2011, 10:06
I would like to subclass QPushButton so that it supports word wrapping similar to how QLabel word wraps it also still needs to support StyleSheets.

I know I need to override the paintevent but I don't have any experience with low level painting and dealing with styles.

This is the current QPushbutton paintevent



void QPushButton::paintEvent(QPaintEvent *)
{
QStylePainter p(this);
QStyleOptionButton option;
initStyleOption(&option);
p.drawControl(QStyle::CE_PushButton, option);
}


looking at this I think what I think I need to do is something like this



void WrappingPushButton::paintEvent(QPaintEvent *)
{
QStylePainter p(this);
QStyleOptionButton option;
initStyleOption(&option);

QTextDocument doc;
//How do I set the text style (font size etc) in the document to that defined on
//the push button via a stylesheet?
doc.setText(text());
doc.setTextWidth(width());

//Is it safe to change the height of a widget within it's own paintevent?
setHeight(doc.height());

//In this function how to prevent it from drawing the bad one lined text?
p.drawControl(QStyle::CE_PushButton, option);

//How to get the innerRectangle of the QPushButton so that I don't draw over
//any padding defined in the style.
QRect innerRectangle;

QPainter painter(this);
doc.drawContents( painter, innerRectangle );
}


Guidance is welcome and if this approach is completely wrong please let me know that too ;)

wysota
9th June 2011, 11:15
Close enough. You need to query QStyle API for things like margins and you'll also want to remove the text to draw from the "option" object. You also need to calculate the proper size of the button to hold all the text and return it from within sizeHint().

Berryblue031
9th June 2011, 12:26
Thank you Wysota

I am having a small problem though setting the font on the QTextDocument.
From QStyleOption I can get the QFontMetrics, but I can't figure out how to apply them to the QTextDocument which only has setFont(QFont).

I dont want to use QPushButton::font() because I don't believe that takes stylesheet fonts into consideration.

wysota
9th June 2011, 12:57
QTextDocument usually has its own font that can be set independently from the widget. If you just want word wrapping then using QPainter::drawText() with the wrapping option enabled might be an easier solution. I don't think you'll be able to retain the stylesheet font anyway so I wouldn't bother trying.

Berryblue031
9th June 2011, 13:13
Hmmm but without the QTextDocument how can I determine the necessary height of the widget?

wysota
9th June 2011, 14:39
QFontMetrics::boundingRect() has the needed capabilities.

Berryblue031
9th June 2011, 14:58
Ok I am soooo close, the only thing I am missing is how to find the actual padding on the QPushButton (i.e. the space between the buttons drawn border and the area for the text)



QSize WrappingPushButton::sizeHint() const
{
QSize size = QPushButton::sizeHint();
QStyleOptionButton option;
initStyleOption(&option);

QMargins m = getPaddingToLabel();
QRect r = option.fontMetrics.boundingRect(0, 0, width() - m.right() - m.left(), 2147483647, mTextFlags, text());

size.setHeight(r.height() + m.top() + m.bottom());

return size;
}

void WrappingPushButton::paintEvent(QPaintEvent* e)
{
QStylePainter p(this);
QStyleOptionButton option;
initStyleOption(&option);

//This will prevent the style from drawing the text
option.text = "";

//Draw button
p.drawControl(QStyle::CE_PushButton, option);

//Draw Text
QMargins m = getPaddingToLabel();
QRect innerRectangle = QRect(m.right(), m.top(), width() - m.right() - m.left(), height() - m.top() - m.bottom());
p.drawText(innerRectangle, mTextFlags, text());
}

QMargins WrappingPushButton::getPaddingToLabel() const
{
//TODO: How to get the real margins!
return QMargins(10,10,10,10);
}

wysota
9th June 2011, 15:14
QStyle::pixelMetric() with QStyle::PM_ButtonMargin.

By the way:

http://www.wysota.eu.org/wwwidgets/doc/html/qwwrichtextbutton.html