Turn elidedlabel example into a code snippet
The example is 90% boiler plate for subclassing QFrame and providing a bit of GUI to change the size of the label using sliders. The interesting bit is a block of 25 lines of code, so turn those into a snippet and add that to the QTextLayout overview documentation. Fixes: QTBUG-111011 Pick-to: 6.5 Change-Id: I6e97b2ea47b553c8d998ad185cfac006721ef7ee Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
This commit is contained in:
parent
31518f1a4e
commit
55f2b448b0
Binary file not shown.
Before Width: | Height: | Size: 24 KiB |
@ -1,131 +0,0 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
|
||||
|
||||
/*!
|
||||
\example widgets/elidedlabel
|
||||
\title Elided Label Example
|
||||
|
||||
\brief This example creates a widget similar to QLabel, that elides the last
|
||||
visible line, if the text is too long to fit the widget's geometry.
|
||||
|
||||
\image elidedlabel-example.png Elided Label example on XPressMusic 5800
|
||||
|
||||
When text of varying length has to be displayed in a uniformly sized
|
||||
area, for instance within a list or grid view where all list items have the
|
||||
same size, it can be useful to give the user a visual clue when not all
|
||||
text is visible. QLabel can elide text that doesn't fit within it, but only
|
||||
in one line. The \c ElidedLabel widget shown in this example word wraps its
|
||||
text by its width, and elides the last visible line if some text is left
|
||||
out. \c TestWidget gives control to the features of \c ElidedWidget and
|
||||
forms the example application.
|
||||
|
||||
|
||||
\section1 ElidedLabel Class Definition
|
||||
|
||||
Like QLabel, \c ElidedLabel inherits from QFrame. Here's the definition of
|
||||
the \c ElidedLabel class:
|
||||
|
||||
|
||||
\snippet widgets/elidedlabel/elidedlabel.h 0
|
||||
|
||||
The \c isElided property depends the font, text content and geometry of the
|
||||
widget. Whenever any of these change, the \c elisionChanged() signal might
|
||||
trigger. We cache the current elision value in \c elided, so that it
|
||||
doesn't have to be recomputed every time it's asked for.
|
||||
|
||||
|
||||
\section1 ElidedLabel Class Implementation
|
||||
|
||||
Except for initializing the member variables, the constructor sets the size
|
||||
policy to be horizontally expanding, since it's meant to fill the width of
|
||||
its container and grow vertically.
|
||||
|
||||
\snippet widgets/elidedlabel/elidedlabel.cpp 0
|
||||
|
||||
Changing the \c content require a repaint of the widget.
|
||||
|
||||
\snippet widgets/elidedlabel/elidedlabel.cpp 1
|
||||
|
||||
QTextLayout is used in the \c paintEvent() to divide the \c content into
|
||||
lines, that wrap on word boundaries. Each line, except the last visible
|
||||
one, is drawn \c lineSpacing pixels below the previous one. The \c draw()
|
||||
method of QTextLine will draw the line using the coordinate point as the
|
||||
top left corner.
|
||||
|
||||
\snippet widgets/elidedlabel/elidedlabel.cpp 2
|
||||
|
||||
Unfortunately, QTextLayout does not elide text, so the last visible line
|
||||
has to be treated differently. This last line is elided if it is too wide.
|
||||
The \c drawText() method of QPainter draws the text starting from the base
|
||||
line, which is \c ascecnt() pixels below the last drawn line.
|
||||
|
||||
Finally, one more line is created to see if everything fit on this line.
|
||||
|
||||
\snippet widgets/elidedlabel/elidedlabel.cpp 3
|
||||
|
||||
If the text was elided and wasn't before or vice versa, cache it in
|
||||
\c elided and emit the change.
|
||||
|
||||
\snippet widgets/elidedlabel/elidedlabel.cpp 4
|
||||
|
||||
|
||||
\section1 TestWidget Class Definition
|
||||
|
||||
\c TestWidget is a QWidget and is the main window of the example. It
|
||||
contains an \c ElidedLabel which can be resized with two QSlider widgets.
|
||||
|
||||
\snippet widgets/elidedlabel/testwidget.h 0
|
||||
|
||||
\section1 TestWidget Class Implementation
|
||||
|
||||
The constructor initializes the whole widget. Strings of different length
|
||||
are stored in \c textSamples. The user is able to switch between these.
|
||||
|
||||
\snippet widgets/elidedlabel/testwidget.cpp 0
|
||||
|
||||
An \c ElidedLabel is created to contain the first of the sample strings.
|
||||
The frame is made visible to make it easier to see the actual size of the
|
||||
widget.
|
||||
|
||||
\snippet widgets/elidedlabel/testwidget.cpp 1
|
||||
|
||||
The buttons and the elision label are created. By connecting the
|
||||
\c elisionChanged() signal to the \c setVisible() slot of the \c label,
|
||||
it will act as an indicator to when the text is elided or not. This signal
|
||||
could, for instance, be used to make a "More" button visible, or similar.
|
||||
|
||||
\snippet widgets/elidedlabel/testwidget.cpp 2
|
||||
|
||||
The \c widthSlider and \c heightSlider specify the size of the
|
||||
\c elidedText. Since the y-axis is inverted, the \c heightSlider has to be
|
||||
inverted to act appropriately.
|
||||
|
||||
\snippet widgets/elidedlabel/testwidget.cpp 3
|
||||
|
||||
The components are all stored in a QGridLayout, which is made the layout of
|
||||
the \c TestWidget.
|
||||
|
||||
\snippet widgets/elidedlabel/testwidget.cpp 4
|
||||
|
||||
The \c widthSlider and \c heightSlider have the exact same length as the
|
||||
dimensions of the \c elidedText. The maximum value for both of them is
|
||||
thus their lengths, and each tick indicates one pixel.
|
||||
|
||||
\snippet widgets/elidedlabel/testwidget.cpp 6
|
||||
|
||||
The \c switchText() slot simply cycles through all the available sample
|
||||
texts.
|
||||
|
||||
\snippet widgets/elidedlabel/testwidget.cpp 7
|
||||
|
||||
These slots set the width and height of the \c elided text, in response to
|
||||
changes in the sliders.
|
||||
|
||||
\section1 The \c main() Function
|
||||
|
||||
The \c main() function creates an instance of \c TestWidget fullscreen and
|
||||
enters the message loop.
|
||||
|
||||
\snippet widgets/elidedlabel/main.cpp 0
|
||||
*/
|
||||
|
@ -7,7 +7,6 @@ qt_internal_add_example(calendarwidget)
|
||||
qt_internal_add_example(charactermap)
|
||||
qt_internal_add_example(codeeditor)
|
||||
qt_internal_add_example(digitalclock)
|
||||
qt_internal_add_example(elidedlabel)
|
||||
qt_internal_add_example(groupbox)
|
||||
qt_internal_add_example(icons)
|
||||
qt_internal_add_example(imageviewer)
|
||||
|
@ -1,38 +0,0 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(elidedlabel LANGUAGES CXX)
|
||||
|
||||
if(NOT DEFINED INSTALL_EXAMPLESDIR)
|
||||
set(INSTALL_EXAMPLESDIR "examples")
|
||||
endif()
|
||||
|
||||
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/widgets/elidedlabel")
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
|
||||
|
||||
qt_standard_project_setup()
|
||||
|
||||
qt_add_executable(elidedlabel
|
||||
elidedlabel.cpp elidedlabel.h
|
||||
main.cpp
|
||||
testwidget.cpp testwidget.h
|
||||
)
|
||||
|
||||
set_target_properties(elidedlabel PROPERTIES
|
||||
WIN32_EXECUTABLE TRUE
|
||||
MACOSX_BUNDLE TRUE
|
||||
)
|
||||
|
||||
target_link_libraries(elidedlabel PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::Gui
|
||||
Qt6::Widgets
|
||||
)
|
||||
|
||||
install(TARGETS elidedlabel
|
||||
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
)
|
@ -1,74 +0,0 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "elidedlabel.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QSizePolicy>
|
||||
#include <QTextLayout>
|
||||
|
||||
//! [0]
|
||||
ElidedLabel::ElidedLabel(const QString &text, QWidget *parent)
|
||||
: QFrame(parent)
|
||||
, elided(false)
|
||||
, content(text)
|
||||
{
|
||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||
}
|
||||
//! [0]
|
||||
|
||||
//! [1]
|
||||
void ElidedLabel::setText(const QString &newText)
|
||||
{
|
||||
content = newText;
|
||||
update();
|
||||
}
|
||||
//! [1]
|
||||
|
||||
//! [2]
|
||||
void ElidedLabel::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
QFrame::paintEvent(event);
|
||||
|
||||
QPainter painter(this);
|
||||
QFontMetrics fontMetrics = painter.fontMetrics();
|
||||
|
||||
bool didElide = false;
|
||||
int lineSpacing = fontMetrics.lineSpacing();
|
||||
int y = 0;
|
||||
|
||||
QTextLayout textLayout(content, painter.font());
|
||||
textLayout.beginLayout();
|
||||
forever {
|
||||
QTextLine line = textLayout.createLine();
|
||||
|
||||
if (!line.isValid())
|
||||
break;
|
||||
|
||||
line.setLineWidth(width());
|
||||
int nextLineY = y + lineSpacing;
|
||||
|
||||
if (height() >= nextLineY + lineSpacing) {
|
||||
line.draw(&painter, QPoint(0, y));
|
||||
y = nextLineY;
|
||||
//! [2]
|
||||
//! [3]
|
||||
} else {
|
||||
QString lastLine = content.mid(line.textStart());
|
||||
QString elidedLastLine = fontMetrics.elidedText(lastLine, Qt::ElideRight, width());
|
||||
painter.drawText(QPoint(0, y + fontMetrics.ascent()), elidedLastLine);
|
||||
line = textLayout.createLine();
|
||||
didElide = line.isValid();
|
||||
break;
|
||||
}
|
||||
}
|
||||
textLayout.endLayout();
|
||||
//! [3]
|
||||
|
||||
//! [4]
|
||||
if (didElide != elided) {
|
||||
elided = didElide;
|
||||
emit elisionChanged(didElide);
|
||||
}
|
||||
}
|
||||
//! [4]
|
@ -1,36 +0,0 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef ELIDEDLABEL_H
|
||||
#define ELIDEDLABEL_H
|
||||
|
||||
#include <QFrame>
|
||||
#include <QString>
|
||||
|
||||
//! [0]
|
||||
class ElidedLabel : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString text READ text WRITE setText)
|
||||
Q_PROPERTY(bool isElided READ isElided)
|
||||
|
||||
public:
|
||||
explicit ElidedLabel(const QString &text, QWidget *parent = nullptr);
|
||||
|
||||
void setText(const QString &text);
|
||||
const QString & text() const { return content; }
|
||||
bool isElided() const { return elided; }
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
signals:
|
||||
void elisionChanged(bool elided);
|
||||
|
||||
private:
|
||||
bool elided;
|
||||
QString content;
|
||||
};
|
||||
//! [0]
|
||||
|
||||
#endif // TEXTWRAPPINGWIDGET_H
|
@ -1,20 +0,0 @@
|
||||
# Nokia Qt Examples: elided label example
|
||||
|
||||
QT += core gui widgets
|
||||
requires(qtConfig(combobox))
|
||||
|
||||
TARGET = elidedlabel
|
||||
TEMPLATE = app
|
||||
|
||||
SOURCES += \
|
||||
main.cpp\
|
||||
testwidget.cpp \
|
||||
elidedlabel.cpp
|
||||
|
||||
HEADERS += \
|
||||
testwidget.h \
|
||||
elidedlabel.h
|
||||
|
||||
# install
|
||||
target.path = $$[QT_INSTALL_EXAMPLES]/widgets/widgets/elidedlabel
|
||||
INSTALLS += target
|
@ -1,16 +0,0 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "testwidget.h"
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
//! [0]
|
||||
int main( int argc, char *argv[] )
|
||||
{
|
||||
QApplication application( argc, argv );
|
||||
TestWidget w;
|
||||
w.showFullScreen();
|
||||
return application.exec();
|
||||
}
|
||||
//! [0]
|
@ -1,119 +0,0 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "testwidget.h"
|
||||
#include "elidedlabel.h"
|
||||
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
|
||||
//! [0]
|
||||
TestWidget::TestWidget(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
const QString romeo = tr(
|
||||
"But soft, what light through yonder window breaks? / "
|
||||
"It is the east, and Juliet is the sun. / "
|
||||
"Arise, fair sun, and kill the envious moon, / "
|
||||
"Who is already sick and pale with grief / "
|
||||
"That thou, her maid, art far more fair than she."
|
||||
);
|
||||
|
||||
const QString macbeth = tr(
|
||||
"To-morrow, and to-morrow, and to-morrow, / "
|
||||
"Creeps in this petty pace from day to day, / "
|
||||
"To the last syllable of recorded time; / "
|
||||
"And all our yesterdays have lighted fools / "
|
||||
"The way to dusty death. Out, out, brief candle! / "
|
||||
"Life's but a walking shadow, a poor player, / "
|
||||
"That struts and frets his hour upon the stage, / "
|
||||
"And then is heard no more. It is a tale / "
|
||||
"Told by an idiot, full of sound and fury, / "
|
||||
"Signifying nothing."
|
||||
);
|
||||
|
||||
const QString harry = tr("Feeling lucky, punk?");
|
||||
|
||||
textSamples << romeo << macbeth << harry;
|
||||
//! [0]
|
||||
|
||||
//! [1]
|
||||
sampleIndex = 0;
|
||||
elidedText = new ElidedLabel(textSamples[sampleIndex], this);
|
||||
elidedText->setFrameStyle(QFrame::Box);
|
||||
//! [1]
|
||||
|
||||
//! [2]
|
||||
QPushButton *switchButton = new QPushButton(tr("Switch text"));
|
||||
connect(switchButton, &QPushButton::clicked, this, &TestWidget::switchText);
|
||||
|
||||
QPushButton *exitButton = new QPushButton(tr("Exit"));
|
||||
connect(exitButton, &QPushButton::clicked, this, &TestWidget::close);
|
||||
|
||||
QLabel *label = new QLabel(tr("Elided"));
|
||||
label->setVisible(elidedText->isElided());
|
||||
connect(elidedText, &ElidedLabel::elisionChanged, label, &QLabel::setVisible);
|
||||
//! [2]
|
||||
|
||||
//! [3]
|
||||
widthSlider = new QSlider(Qt::Horizontal);
|
||||
widthSlider->setMinimum(0);
|
||||
connect(widthSlider, &QSlider::valueChanged, this, &TestWidget::onWidthChanged);
|
||||
|
||||
heightSlider = new QSlider(Qt::Vertical);
|
||||
heightSlider->setInvertedAppearance(true);
|
||||
heightSlider->setMinimum(0);
|
||||
connect(heightSlider, &QSlider::valueChanged, this, &TestWidget::onHeightChanged);
|
||||
//! [3]
|
||||
|
||||
//! [4]
|
||||
QGridLayout *layout = new QGridLayout;
|
||||
layout->addWidget(label, 0, 1, Qt::AlignCenter);
|
||||
layout->addWidget(switchButton, 0, 2);
|
||||
layout->addWidget(exitButton, 0, 3);
|
||||
layout->addWidget(widthSlider, 1, 1, 1, 3);
|
||||
layout->addWidget(heightSlider, 2, 0);
|
||||
layout->addWidget(elidedText, 2, 1, 1, 3, Qt::AlignTop | Qt::AlignLeft);
|
||||
|
||||
setLayout(layout);
|
||||
//! [4]
|
||||
}
|
||||
|
||||
//! [6]
|
||||
void TestWidget::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
|
||||
int maxWidth = widthSlider->width();
|
||||
widthSlider->setMaximum(maxWidth);
|
||||
widthSlider->setValue(maxWidth / 2);
|
||||
|
||||
int maxHeight = heightSlider->height();
|
||||
heightSlider->setMaximum(maxHeight);
|
||||
heightSlider->setValue(maxHeight / 2);
|
||||
|
||||
elidedText->setFixedSize(widthSlider->value(), heightSlider->value());
|
||||
}
|
||||
//! [6]
|
||||
|
||||
//! [7]
|
||||
void TestWidget::switchText()
|
||||
{
|
||||
sampleIndex = (sampleIndex + 1) % textSamples.size();
|
||||
elidedText->setText(textSamples.at(sampleIndex));
|
||||
}
|
||||
//! [7]
|
||||
|
||||
//! [8]
|
||||
void TestWidget::onWidthChanged(int width)
|
||||
{
|
||||
elidedText->setFixedWidth(width);
|
||||
}
|
||||
|
||||
void TestWidget::onHeightChanged(int height)
|
||||
{
|
||||
elidedText->setFixedHeight(height);
|
||||
}
|
||||
//! [8]
|
||||
|
@ -1,38 +0,0 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef TESTWIDGET_H
|
||||
#define TESTWIDGET_H
|
||||
|
||||
#include <QSlider>
|
||||
#include <QStringList>
|
||||
#include <QWidget>
|
||||
|
||||
class ElidedLabel;
|
||||
|
||||
//! [0]
|
||||
class TestWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TestWidget(QWidget *parent = nullptr);
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
|
||||
private slots:
|
||||
void switchText();
|
||||
void onWidthChanged(int width);
|
||||
void onHeightChanged(int height);
|
||||
|
||||
private:
|
||||
int sampleIndex;
|
||||
QStringList textSamples;
|
||||
ElidedLabel *elidedText;
|
||||
QSlider *heightSlider;
|
||||
QSlider *widthSlider;
|
||||
};
|
||||
//! [0]
|
||||
|
||||
#endif // TESTWIDGET_H
|
@ -5,7 +5,6 @@ SUBDIRS = analogclock \
|
||||
charactermap \
|
||||
codeeditor \
|
||||
digitalclock \
|
||||
elidedlabel \
|
||||
groupbox \
|
||||
icons \
|
||||
imageviewer \
|
||||
|
@ -10,6 +10,7 @@ namespace src_gui_text_qtextlayout {
|
||||
struct Wrapper : public QPaintDevice
|
||||
{
|
||||
void wrapper1();
|
||||
void elided();
|
||||
};
|
||||
QTextLayout textLayout;
|
||||
|
||||
@ -24,7 +25,7 @@ int leading = fontMetrics.leading();
|
||||
qreal height = 0;
|
||||
textLayout.setCacheEnabled(true);
|
||||
textLayout.beginLayout();
|
||||
while (1) {
|
||||
while (true) {
|
||||
QTextLine line = textLayout.createLine();
|
||||
if (!line.isValid())
|
||||
break;
|
||||
@ -49,4 +50,41 @@ textLayout.draw(&painter, QPoint(0, 0));
|
||||
|
||||
} // Wrapper::wrapper1
|
||||
|
||||
void Wrapper::elided() {
|
||||
|
||||
QString content;
|
||||
|
||||
//! [elided]
|
||||
QPainter painter(this);
|
||||
QFontMetrics fontMetrics = painter.fontMetrics();
|
||||
|
||||
int lineSpacing = fontMetrics.lineSpacing();
|
||||
int y = 0;
|
||||
|
||||
QTextLayout textLayout(content, painter.font());
|
||||
textLayout.beginLayout();
|
||||
while (true) {
|
||||
QTextLine line = textLayout.createLine();
|
||||
|
||||
if (!line.isValid())
|
||||
break;
|
||||
|
||||
line.setLineWidth(width());
|
||||
const int nextLineY = y + lineSpacing;
|
||||
|
||||
if (height() >= nextLineY + lineSpacing) {
|
||||
line.draw(&painter, QPoint(0, y));
|
||||
y = nextLineY;
|
||||
} else {
|
||||
const QString lastLine = content.mid(line.textStart());
|
||||
const QString elidedLastLine = fontMetrics.elidedText(lastLine, Qt::ElideRight, width());
|
||||
painter.drawText(QPoint(0, y + fontMetrics.ascent()), elidedLastLine);
|
||||
line = textLayout.createLine();
|
||||
break;
|
||||
}
|
||||
}
|
||||
textLayout.endLayout();
|
||||
//! [elided]
|
||||
}
|
||||
|
||||
} // src_gui_text_qtextlayout
|
||||
|
@ -265,6 +265,10 @@ Qt::LayoutDirection QTextInlineObject::textDirection() const
|
||||
The text can then be rendered by calling the layout's draw() function:
|
||||
\snippet code/src_gui_text_qtextlayout.cpp 1
|
||||
|
||||
It is also possible to draw each line individually, for instance to draw
|
||||
the last line that fits into a widget elided:
|
||||
\snippet code/src_gui_text_qtextlayout.cpp elided
|
||||
|
||||
For a given position in the text you can find a valid cursor position with
|
||||
isValidCursorPosition(), nextCursorPosition(), and previousCursorPosition().
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user