This post is actually a sequel to this thread. I created a new thread to give some visibility to a solution which might have some interest; please advise if this breaks some forum policy.
THE PROBLEM
Vertically centering a text in a QWebView widget is not very complex; with some experiments, I found that the following HTML text is enough:
<html><body><table height='100%' width='100%'><tr><td valign='middle' style='text-align: center;'>%1</td></tr></table></body></html>
<html><body><table height='100%' width='100%'><tr><td valign='middle' style='text-align: center;'>%1</td></tr></table></body></html>
To copy to clipboard, switch view to plain text mode
where %1 is supposed to be .arg'ed with the text to display.
If a (read-only) QTextEdit is used instead of a QWebView, neither the above HTML not any other combination of HTML tags I could think of achieves the intended result: the text always remains at the top of the widget. The point in using QTextEdit instead of QWebView is to avoid the overhead of a full-fledged web browser (also in terms of executable file size and, IIUC, in terms of licensing) when only a few words of styled text have to be shown in a small widget.
I tried a number of other tricks and none worked: QTextEdit::setAlignment(Qt::AlignVCenter) does not work (and is in fact excluded from the relevant documentation). QTextFormatChar::setVerticalAlignment(QTextCharFor mat::AlignMiddle) applies to a different purpose. Attempting to set the page size of the QTextEdit document to the size of the widget (myTextEdit.document()->setPageSize( QSize(myTextEdit.width(), myTextEdit.height()) ) brought no result. In fact I made more attempts than I can remember...
I picture the situation imagining that QTextEdit has no concept of itself as a physical frame; it is rather a viewport on a document which is only 'tall' as itself and then centering vertically makes no sense (in the middle of what? Of a document, which is only high as the text it contains?). Actual Qt code may be different, but this is the picture I made for myself.
THE SOLUTION
The basic idea is to simulate the vertical centering by playing with the top margin of the document root frame. This is the function I use:
{
// retrieve total text document height and widget height
int nDocHeight = (int)pTextEdit->document()->size().height();
if(nDocHeight == 0) // if no document, do nothing
return;
int nCtrlHeight= pTextEdit->height(); // the TextEdit control height
// access the document top frame and its format
QTextFrame * pFrame
= pTextEdit
->document
()->rootFrame
();
// get current top margin and compute the 'black' height of the document
int nTopMargin = (int)frameFmt.topMargin(); // the frame top margin
int nBBDocH = nDocHeight - nTopMargin; // the height of the document 'bounding box'
// compute and set appropriate frame top margin
if(nCtrlHeight <= nBBDocH) // if the control is shorter than the document
nTopMargin = 2; // set a nominal top margin
else // if the control is taller than the document
nTopMargin = (nCtrlHeight - nBBDocH)/2 - 2; // set half of the excess as top margin
frameFmt.setTopMargin(nTopMargin);
// SET A BORDER!! (using the same colour as the background)
frameFmt.setBorder(1);
frameFmt.
setBorderBrush(QColor(0xFFFFFF
));
pFrame->setFrameFormat(frameFmt); // apply the new format
}
void ValignMiddle(QTextEdit * pTextEdit)
{
// retrieve total text document height and widget height
int nDocHeight = (int)pTextEdit->document()->size().height();
if(nDocHeight == 0) // if no document, do nothing
return;
int nCtrlHeight= pTextEdit->height(); // the TextEdit control height
// access the document top frame and its format
QTextFrame * pFrame = pTextEdit->document()->rootFrame();
QTextFrameFormat frameFmt = pFrame->frameFormat();
// get current top margin and compute the 'black' height of the document
int nTopMargin = (int)frameFmt.topMargin(); // the frame top margin
int nBBDocH = nDocHeight - nTopMargin; // the height of the document 'bounding box'
// compute and set appropriate frame top margin
if(nCtrlHeight <= nBBDocH) // if the control is shorter than the document
nTopMargin = 2; // set a nominal top margin
else // if the control is taller than the document
nTopMargin = (nCtrlHeight - nBBDocH)/2 - 2; // set half of the excess as top margin
frameFmt.setTopMargin(nTopMargin);
// SET A BORDER!! (using the same colour as the background)
frameFmt.setBorder(1);
frameFmt.setBorderBrush(QColor(0xFFFFFF));
pFrame->setFrameFormat(frameFmt); // apply the new format
}
To copy to clipboard, switch view to plain text mode
(nBBDocH should probably also discount the root frame bottom margin; for my application, it made no real difference. The "-2" in line 21 is a rough visual correction as a bottom margin slightly larger than the top margin 'looks better').
The real TRICK are lines 25 and 26 (it took me two days of trials and errors to discover it). The frame top margin is only obeyed if the frame has some border (if I do not wnat it to show, I use the same colour as the background); if no border is defined, the top margin is ignored and the text appears at the top of the widget.
The above code can of course be improved, but works; I use it in several contexts. However, it needs two additions:
- the top margin needs to be re-computed when the widget is resized.
- the actual document height is not computed until after the widget is initially shown (this only applies if the QTextEdit widget is filled with static text when the dialogue it belongs to is initially constructed).
So two event handlers have to be added:
{
// when the dialogue box is resized, the widget is probably resized too.
// Re-display the QTextEdit contents from scratch, as the document / margins dimensions are obsolete
DisplayTextEditText(/* needed params */);
ValignMiddle(ui->myTextEdit);
}
void MyDialogueClass
::showEvent(QShowEvent * event
) {
// once the widget is shown, recompute the top margin from the now-actual doc sizes
ValignMiddle(ui->myTextEdit);
}
void MyDialogueClass::resizeEvent(QResizeEvent * event)
{
// when the dialogue box is resized, the widget is probably resized too.
// Re-display the QTextEdit contents from scratch, as the document / margins dimensions are obsolete
DisplayTextEditText(/* needed params */);
ValignMiddle(ui->myTextEdit);
}
void MyDialogueClass::showEvent(QShowEvent * event)
{
// once the widget is shown, recompute the top margin from the now-actual doc sizes
ValignMiddle(ui->myTextEdit);
}
To copy to clipboard, switch view to plain text mode
With these three methods, the text ends up properly centered in the middle of the QTextEdit widget.
In most situations, it probably makes sense to subclass QTextEdit and include these 3 methods in the subclassed widget itself (I didn't show it, for the sake of simplicity).
A little question to end: vertical centering of a text in a widget is a rather common need; is all the above really needed?
Cheers,
M.
Bookmarks