KWidgetsAddons

ksqueezedtextlabel.cpp
1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2000 Ronny Standtke <Ronny.Standtke@gmx.de>
4
5 SPDX-License-Identifier: LGPL-2.0-only
6*/
7
8#include "ksqueezedtextlabel.h"
9#include <QAction>
10#include <QApplication>
11#include <QClipboard>
12#include <QContextMenuEvent>
13#include <QMenu>
14#include <QRegularExpression>
15#include <QScreen>
16#include <QTextDocument>
17
18class KSqueezedTextLabelPrivate
19{
20public:
21 void copyFullText()
22 {
24 }
25
26 QString fullText;
27 Qt::TextElideMode elideMode;
28};
29
31 : QLabel(parent)
32 , d(new KSqueezedTextLabelPrivate)
33{
35 d->fullText = text;
36 d->elideMode = Qt::ElideMiddle;
38}
39
41 : QLabel(parent)
42 , d(new KSqueezedTextLabelPrivate)
43{
45 d->elideMode = Qt::ElideMiddle;
46}
47
48KSqueezedTextLabel::~KSqueezedTextLabel() = default;
49
54
56{
58 sh.setWidth(-1);
59 return sh;
60}
61
63{
64 if (!isSqueezed()) {
65 return QLabel::sizeHint();
66 }
67 int maxWidth = screen()->geometry().width() * 3 / 4;
69 // Do exactly like qlabel.cpp to avoid slight differences in results
70 // (see https://invent.kde.org/frameworks/kwidgetsaddons/-/merge_requests/100)
71 int textWidth = fm.boundingRect(0, 0, 2000, 2000, Qt::AlignAbsolute | Qt::TextExpandTabs | Qt::AlignLeft, d->fullText).width();
72 if (textWidth > maxWidth) {
73 textWidth = maxWidth;
74 }
75 const int chromeWidth = width() - contentsRect().width();
76 return QSize(textWidth + chromeWidth, QLabel::sizeHint().height());
77}
78
80{
81 QLabel::setIndent(indent);
83}
84
86{
87 QLabel::setMargin(margin);
89}
90
92{
93 d->fullText = text;
95}
96
98{
99 d->fullText.clear();
101}
102
104{
106 const int labelWidth = contentsRect().width();
108 bool squeezed = false;
109 const auto textLines = d->fullText.split(QLatin1Char('\n'));
110 squeezedLines.reserve(textLines.size());
111 for (const QString &line : textLines) {
112 int lineWidth = fm.boundingRect(line).width();
113 if (lineWidth > labelWidth) {
114 squeezed = true;
115 squeezedLines << fm.elidedText(line, d->elideMode, labelWidth);
116 } else {
117 squeezedLines << line;
118 }
119 }
120
121 if (squeezed) {
123 setToolTip(d->fullText);
124 } else {
125 QLabel::setText(d->fullText);
127 }
128}
129
131{
132 // calculation according to API docs for QLabel::indent
133 const int margin = this->margin();
134 int indent = this->indent();
135 if (indent < 0) {
136 if (frameWidth() == 0) {
137 indent = 0;
138 } else {
139 indent = fontMetrics().horizontalAdvance(QLatin1Char('x')) / 2 - margin;
140 }
141 }
142
143 QRect result = QLabel::contentsRect();
144 if (indent > 0) {
145 const int alignment = this->alignment();
146 if (alignment & Qt::AlignLeft) {
147 result.setLeft(result.left() + indent);
148 }
149 if (alignment & Qt::AlignTop) {
150 result.setTop(result.top() + indent);
151 }
153 result.setRight(result.right() - indent);
154 }
156 result.setBottom(result.bottom() - indent);
157 }
158 }
159
160 result.adjust(margin, margin, -margin, -margin);
161 return result;
162}
163
165{
166 // save fullText and restore it
167 QString tmpFull(d->fullText);
169 d->fullText = tmpFull;
170}
171
172Qt::TextElideMode KSqueezedTextLabel::textElideMode() const
173{
174 return d->elideMode;
175}
176
178{
179 d->elideMode = mode;
181}
182
184{
185 return d->fullText;
186}
187
189{
190 return d->fullText != text();
191}
192
194{
195 // We want to reimplement "Copy" to include the elided text.
196 // But this means reimplementing the full popup menu, so no more
197 // copy-link-address or copy-selection support anymore, since we
198 // have no access to the QTextDocument.
199 // Maybe we should have a boolean flag in KSqueezedTextLabel itself for
200 // whether to show the "Copy Full Text" custom popup?
201 // For now I chose to show it when the text is squeezed; when it's not, the
202 // standard popup menu can do the job (select all, copy).
203
204 if (isSqueezed()) {
205 QMenu menu(this);
206
207 QAction *act = new QAction(QIcon::fromTheme(QStringLiteral("edit-copy")), tr("&Copy Full Text", "@action:inmenu"), &menu);
208 connect(act, &QAction::triggered, this, [this]() {
209 d->copyFullText();
210 });
211 menu.addAction(act);
212
213 ev->accept();
214 menu.exec(ev->globalPos());
215 } else {
217 }
218}
219
221{
222 if (QApplication::clipboard()->supportsSelection() //
224 && ev->button() == Qt::LeftButton //
225 && !d->fullText.isEmpty() //
226 && hasSelectedText()) {
227 // Expand "..." when selecting with the mouse
229 const QChar ellipsisChar(0x2026); // from qtextengine.cpp
230 const int dotsPos = txt.indexOf(ellipsisChar);
231 if (dotsPos > -1) {
232 // Ex: abcde...yz, selecting de...y (selectionStart=3)
233 // charsBeforeSelection = selectionStart = 2 (ab)
234 // charsAfterSelection = 1 (z)
235 // final selection length= 26 - 2 - 1 = 23
236 const int start = selectionStart();
237 int charsAfterSelection = text().length() - start - selectedText().length();
238 txt = d->fullText;
239 // Strip markup tags
240 if (textFormat() == Qt::RichText //
242 txt.remove(QRegularExpression(QStringLiteral("<[^>]*>")));
243 // account for stripped characters
244 charsAfterSelection -= d->fullText.length() - txt.length();
245 }
246 txt = txt.mid(selectionStart(), txt.length() - start - charsAfterSelection);
247 }
249 } else {
251 }
252}
253
254#include "moc_ksqueezedtextlabel.cpp"
KSqueezedTextLabel(QWidget *parent=nullptr)
Default constructor.
virtual void setAlignment(Qt::Alignment)
Overridden for internal reasons; the API remains unaffected.
void resizeEvent(QResizeEvent *) override
Called when widget is resized.
void setTextElideMode(Qt::TextElideMode mode)
Sets the text elide mode.
QSize sizeHint() const override
void clear()
Clears the text.
void setMargin(int margin)
Sets the margin of the label.
void mouseReleaseEvent(QMouseEvent *) override
void contextMenuEvent(QContextMenuEvent *) override
void setIndent(int indent)
Sets the indentation of the label.
void setText(const QString &text)
Sets the text.
QSize minimumSizeHint() const override
void squeezeTextToLabel()
does the dirty work
Q_SCRIPTABLE Q_NOREPLY void start()
void triggered(bool checked)
void setText(const QString &text, Mode mode)
int horizontalAdvance(QChar ch) const const
QClipboard * clipboard()
QIcon fromTheme(const QString &name)
void clear()
virtual void contextMenuEvent(QContextMenuEvent *ev) override
void setIndent(int)
void setMargin(int)
virtual QSize minimumSizeHint() const const override
virtual void mouseReleaseEvent(QMouseEvent *ev) override
int selectionStart() const const
virtual QSize sizeHint() const const override
textInteractionFlags
QAction * addAction(const QIcon &icon, const QString &text, Functor functor, const QKeySequence &shortcut)
QAction * exec()
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
T qobject_cast(QObject *object)
QString tr(const char *sourceText, const char *disambiguation, int n)
void adjust(int dx1, int dy1, int dx2, int dy2)
int bottom() const const
int left() const const
int right() const const
void setBottom(int y)
void setLeft(int x)
void setRight(int x)
void setTop(int y)
int top() const const
int width() const const
bool mightBeRichText(const QString &text)
AlignAbsolute
LeftButton
TextElideMode
TextExpandTabs
RichText
NoTextInteraction
QRect contentsRect() const const
QFontMetrics fontMetrics() const const
QScreen * screen() const const
void setSizePolicy(QSizePolicy)
void setToolTip(const QString &)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:14:43 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.