Qt - Pop up a bubble when mouse is over QRect object - c++

My QRect object is a fixed-sized containter of plain text, when there is too much text I would truncate the text and trail ... at the end. For example, Longlonglonglong is truncated to Longlong.... But I want to display the full-length text in a bubble when mouse pointer is over the rect.
The bubble is like the Go to Google Home:
Is this possible ?

Unfortunately QPainter can't do that for you, the drawText(..) flags don't support it. Thankfully, you can pre-elide the text for it ("eliding" is where you truncate with an elipsis) using QFontMetrics:
QFontMetrics fontM( QApplication::font() );
QRect r( 0, 0, 30, 10 );
QString text = "Longlonglonglong";
QString elidedText = fontM.elidedText( text, Qt::ElideRight, r.width() );
painter->drawText( r, Qt::AlignLeft, elidedText );
When you say "text in a bubble when mouse pointer is over", I presume you mean a tooltip - in which case implement it for the widget as normal and give the full text rather than the elided.

Related

align right-to-left text with QPainter::drawText

I am trying to paint a right-to-left text with QPainter. It however still aligns the text to left despite the fact that it should be right-aligned. Or at least it is right-aligned when displayed in QTextEdit. That am I doing wrong? See the example:
QTextOption option;
option.setTextDirection(Qt::LayoutDirectionAuto);
painter->drawText(rect, "خامل\nخاملخامل", option); // this is just testing text, I have no idea what it means, hopefully it is not something offensive :)
This is what I get. The red arrow shows where it should be aligned.
Of course I cannot use fixed right alignment because I do not whether the text in question is left-to-right or right-to-left text. So it must work for both directions automatically depending on the text.
UPDATE: I want to emphasize that I need a solution which automatically recognizes the language/writing system of the text (left-to-right, such as English, versus right-to-left, such as Arabic) and aligns the text automatically.
you should check your text with isRightToLeft() function.
QString isRightToLeft():
Returns true if the string is read right to left.
This will help you to understand your text language.
I checked the QTextEdit source and understand it uses this function.
void MainWindow::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
QRect rect = QRect(0, 0, width(), height());
QString txt = tr("خامل\nخاملخامل");
// QString txt = tr("ABCD\nABCD");
QTextOption option;
if (txt.isRightToLeft())
{
option.setTextDirection(Qt::RightToLeft);
}
else
{
option.setTextDirection(Qt::LeftToRight);
}
painter.drawText(rect, txt, option);
}

Qt: Draw text with different fonts

I'm using void QPainter::drawText(const QRectF &rectangle, const QString &text, const QTextOption &option = QTextOption() method to draw some text.
It lets me to align text as i wish (in the center of rectangle, for example).
Now, i need to do the same thing, except i need to draw one part of text with
some font and the other part with another.
For example, if text is "Hello world", i want "Hello" to be drawn with Arial and "World" with Times New Roman, but it should still be aligned with the center of rectangle.
What is the best way to achieve that?
you can try with this :
QPainter painter(this);
painter.setFont(QFont("Arial", 12));
painter.drawText(rect(), Qt::AlignCenter, "Hello");

Qt5 QStyledItemDelegate on a QListView removes all the default style

I have some QIcon and QString pairs displayed in a QListview. The whole thing has been set up using the Qt Model/View Programming.
I am displaying labeled icons in this QListView. Items are displayed using the IconMode, Snap and TopToBottom flags. Thus, these are organised into a grid.
I would like to layout all the QListView items vertically and centered. In order to do this, I subclassed the QStyledItemDelegate object, and overloaded the paint method. However, I have three main problems:
Icon labels have been moved (in the QStyledItemDelegate subclasses) and a dotted square appears at its original place.
All the default styles are gone (hover, selection). I know how I can add some again, but I would like to use the default one (Windows style).
Everything is rendered into a grid, even if setGridSize is not called. I would like to use only one "column".
Here is a piece of code:
An extract of the constructor of my custom QListView:
setViewMode(QListView::IconMode);
setMovement(QListView::Snap);
setFlow(QListView::TopToBottom);
setSpacing(5);
setIconSize(QSize(iconSize, iconSize));
setGridSize(QSize(iconSize + 10, iconSize + 10));
setDragEnabled(true);
setAcceptDrops(true);
setDropIndicatorShown(true);
The paint method of the QStyledItemDelegate:
void FramesStyledItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
QStyleOptionViewItemV4 opt = option;
//initStyleOption(&opt, index);
opt.icon = QIcon();
opt.text = QString();
QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &opt, painter);
const QRect r = option.rect;
QIcon icon = qvariant_cast<QIcon>(index.data(Qt::DecorationRole));
QString string = qvariant_cast<QString>(index.data(Qt::DisplayRole));
QPixmap pix = icon.pixmap(r.size());
const QPoint p = QPoint((r.width() - pix.width())/2, (r.height() - pix.height())/2);
painter->drawPixmap(r.topLeft() + p, pix);
painter->drawText(r.center() + p + QPoint(-(string.count() / 2), r.height() / 2), string);
}
If I do not use the initStyleOption shown above, I can remove the
dotted square, but I lose all the default styles.
If I uncomment the initStyleOption, The dotted square appears. I also lose all the default styles.
Here are some screenshots:
The cursor is on item 0 (No hover decoration, no selection decoration).
Item 0 has been selected. A small dotted square appears (initStyleOption has been uncommented).
I have switched to the ListMode. Selection decoration is working but not hover. Again, a small dotted square appears at the original place of the label.
Does someone have an idea? Thanks for your answers.

Reimplementing QStyledItemDelegate::paint - How to obtain subelement coordinates?

A QTreeView is rendered with the help of a custom QStyledItemDelegate::paint method. The intention is to add graphical elements to the nodes, e.g. to draw (and fill) a box around the item texts. The tree items may have check boxes, or not.
The Ruby code below achieves the goal, except that I cannot obtain the coordinates of the text element. An empirical offset (x=29; y=4) serves as a workaround. The super method draws the text on top of the box.
How can I obtain the coordinates of the text element?
Is this the right approach at all, or do I have to use drawText and drawControl instead of calling the superclass paint method? In that case, how do you control the layout of the sub elements?
(This question is not Ruby specific. Answers containing C++ are welcome.)
class ItemDelegate < Qt::StyledItemDelegate
def paint(painter, option, index)
text = index.data.toString
bg_color = Qt::Color.new(Qt::yellow)
fg_color = Qt::Color.new(Qt::black)
offset = Qt::Point.new(29,4)
painter.save
painter.translate(option.rect.topLeft + offset)
recti = Qt::Rect.new(0, 0, option.rect.width, option.rect.height)
rectf = Qt::RectF.new(recti)
margin = 4
bounding = painter.boundingRect(rectf, Qt::AlignLeft, text)
tbox = Qt::RectF.new(Qt::PointF.new(-margin,0), bounding.size)
tbox.width += 2*margin
painter.fillRect(tbox, bg_color)
painter.drawRect(tbox)
painter.restore
super
end
end
Edit: Please find a self-contained example here in this Gist.
I had the same problem in C++. Unfortunately, the workaround on option.rect.* properties seems to be the only way to find the text coords.
Here the paint method of my delegate:
void ThumbnailDelegate::paint(QPainter *p_painter, const QStyleOptionViewItem &p_option, const QModelIndex &p_index) const
{
if(p_index.isValid())
{
const QAbstractItemModel* l_model = p_index.model();
QPen l_text_pen(Qt::darkGray);
QBrush l_brush(Qt::black, Qt::SolidPattern);
/** background rect **/
QPen l_pen;
l_pen.setStyle(Qt::SolidLine);
l_pen.setWidth(4);
l_pen.setBrush(Qt::lightGray);
l_pen.setCapStyle(Qt::RoundCap);
l_pen.setJoinStyle(Qt::RoundJoin);
p_painter->setPen(l_pen);
QRect l_border_rect;
l_border_rect.setX(p_option.rect.x() + 5);
l_border_rect.setY(p_option.rect.y() + 5);
l_border_rect.setWidth(p_option.rect.width() - 16);
l_border_rect.setHeight(p_option.rect.height() - 16);
QPainterPath l_rounded_rect;
l_rounded_rect.addRect(QRectF(l_border_rect));
p_painter->setClipPath(l_rounded_rect);
/** background color for hovered items **/
p_painter->fillPath(l_rounded_rect, l_brush);
p_painter->drawPath(l_rounded_rect);
/** image **/
QPixmap l_pixmap = bytearrayToPixmap(l_model->data(p_index, ImageRole).toByteArray()).scaled(150, 150, Qt::KeepAspectRatio);
QRect l_img_rect = l_border_rect;
int l_img_x = (l_img_rect.width()/2 - l_pixmap.width()/2)+l_img_rect.x();
l_img_rect.setX(l_img_x);
l_img_rect.setY(l_img_rect.y() + 12);
l_img_rect.setWidth(l_pixmap.width());
l_img_rect.setHeight(l_pixmap.height());
p_painter->drawPixmap(l_img_rect, l_pixmap);
/** label **/
QRect l_txt_rect = p_option.rect;
l_txt_rect.setX(l_border_rect.x()+5);
l_txt_rect.setY(l_border_rect.y() + l_border_rect.height() -20);
l_txt_rect.setHeight(20);
l_txt_rect.setWidth(l_txt_rect.width()-20);
QFont l_font;
l_font.setBold(true);
l_font.setPixelSize(12);
p_painter->setFont(l_font);
p_painter->setPen(l_text_pen);
QString l_text = l_model->data(p_index, TextRole).toString();
p_painter->drawText(l_txt_rect, Qt::ElideRight|Qt::AlignHCenter, l_text);
}
else
{
qWarning() << "ThumbnailDelegate::paint() Invalid index!";
}
}
I am not skilled on Ruby but, as you can see, I am using drawPath, drawPixmap and drawText.
Here is the result:
I think it is better to avoid invoking paint from the superclass, since it should be done automatically by Qt and you may break something on the UI lifecycle.

Qt: heightForWidth for word-wrapped text

I have a box with a varying width and a word-wrapped text. I need to set new height every time user changes box's width. The box is displayed by QPainter inside paintEvent(QPaintEvent *) function. There is several solutions, for example current (not very smart, i do this in resizeEvent(QResizeEvent *)):
unsigned new_height = 0; // the height i want to find out.
unsigned given_width = width();
QPainter painter (this); // i need painter, because i want to ask it's default font.
QLabel lab; // the widget that can do word-wrap.
lab.setText( "A word wrapped text" ); // the text
lab.setFont( painter.font() ); // set QPainter's default font.
lab.setWordWrap( true ); // enable word-wrap
new_height = lab.heightForWidth( given_width ); // tada! :)
But the code is overkill:
1) Creading QPainter is not good outside paintEvent(QPaintEvent *);
2) BUT i need QPainter to request what font is default for it to ask metrics for that font.
Should i change my code and do this operation with help of QPainter::boundingRect() inside the paintEvent(QPaintEvent *) function? But i'd like to reduce CPU consumption inside the paintEvent(QPaintEvent *) and calculate new height only when width changed, but not every time it displayed.
What is other solutions for the purpose of subject? QFontMectircs?
I think you have the right idea of using QFontMetrics. The whole idea of the class is to assist the situations like you have here. Take a look at QFontMetricsF::boundingRect()
Use your target paint rectangle as the input rect, but set the height to the max that your widget height. I'd just put something like INT_MAX in it just to be sure. :)