summaryrefslogtreecommitdiffstats
path: root/src/widgets/widgets
diff options
context:
space:
mode:
Diffstat (limited to 'src/widgets/widgets')
-rw-r--r--src/widgets/widgets/qabstractbutton.cpp13
-rw-r--r--src/widgets/widgets/qabstractscrollarea.cpp37
-rw-r--r--src/widgets/widgets/qabstractslider.cpp11
-rw-r--r--src/widgets/widgets/qabstractspinbox.cpp22
-rw-r--r--src/widgets/widgets/qabstractspinbox.h3
-rw-r--r--src/widgets/widgets/qabstractspinbox_p.h4
-rw-r--r--src/widgets/widgets/qbuttongroup.cpp4
-rw-r--r--src/widgets/widgets/qcalendarwidget.cpp17
-rw-r--r--src/widgets/widgets/qcheckbox.cpp27
-rw-r--r--src/widgets/widgets/qcheckbox.h4
-rw-r--r--src/widgets/widgets/qcombobox.cpp451
-rw-r--r--src/widgets/widgets/qcombobox.h15
-rw-r--r--src/widgets/widgets/qcombobox_p.h44
-rw-r--r--src/widgets/widgets/qcommandlinkbutton.cpp8
-rw-r--r--src/widgets/widgets/qdatetimeedit.cpp83
-rw-r--r--src/widgets/widgets/qdatetimeedit.h11
-rw-r--r--src/widgets/widgets/qdatetimeedit_p.h3
-rw-r--r--src/widgets/widgets/qdial.cpp2
-rw-r--r--src/widgets/widgets/qdialogbuttonbox.cpp329
-rw-r--r--src/widgets/widgets/qdialogbuttonbox.h2
-rw-r--r--src/widgets/widgets/qdialogbuttonbox_p.h80
-rw-r--r--src/widgets/widgets/qdockarealayout.cpp93
-rw-r--r--src/widgets/widgets/qdockarealayout_p.h8
-rw-r--r--src/widgets/widgets/qdockwidget.cpp185
-rw-r--r--src/widgets/widgets/qdockwidget.h10
-rw-r--r--src/widgets/widgets/qdockwidget_p.h19
-rw-r--r--src/widgets/widgets/qeffects.cpp7
-rw-r--r--src/widgets/widgets/qfocusframe.cpp8
-rw-r--r--src/widgets/widgets/qfontcombobox.cpp72
-rw-r--r--src/widgets/widgets/qfontcombobox.h2
-rw-r--r--src/widgets/widgets/qgroupbox.cpp11
-rw-r--r--src/widgets/widgets/qkeysequenceedit.cpp16
-rw-r--r--src/widgets/widgets/qlabel.cpp47
-rw-r--r--src/widgets/widgets/qlabel.h9
-rw-r--r--src/widgets/widgets/qlabel_p.h12
-rw-r--r--src/widgets/widgets/qlcdnumber.cpp4
-rw-r--r--src/widgets/widgets/qlineedit.cpp427
-rw-r--r--src/widgets/widgets/qlineedit.h14
-rw-r--r--src/widgets/widgets/qlineedit_p.cpp112
-rw-r--r--src/widgets/widgets/qlineedit_p.h22
-rw-r--r--src/widgets/widgets/qmainwindow.cpp51
-rw-r--r--src/widgets/widgets/qmainwindow.h1
-rw-r--r--src/widgets/widgets/qmainwindowlayout.cpp522
-rw-r--r--src/widgets/widgets/qmainwindowlayout_p.h45
-rw-r--r--src/widgets/widgets/qmdiarea.cpp88
-rw-r--r--src/widgets/widgets/qmdiarea_p.h2
-rw-r--r--src/widgets/widgets/qmdisubwindow.h1
-rw-r--r--src/widgets/widgets/qmenu.cpp60
-rw-r--r--src/widgets/widgets/qmenu_p.h7
-rw-r--r--src/widgets/widgets/qmenubar.cpp47
-rw-r--r--src/widgets/widgets/qmenubar_p.h2
-rw-r--r--src/widgets/widgets/qplaintextedit.cpp222
-rw-r--r--src/widgets/widgets/qplaintextedit.h5
-rw-r--r--src/widgets/widgets/qplaintextedit_p.h21
-rw-r--r--src/widgets/widgets/qpushbutton.cpp18
-rw-r--r--src/widgets/widgets/qpushbutton.h5
-rw-r--r--src/widgets/widgets/qpushbutton_p.h4
-rw-r--r--src/widgets/widgets/qradiobutton.cpp2
-rw-r--r--src/widgets/widgets/qscrollarea.cpp6
-rw-r--r--src/widgets/widgets/qscrollarea_p.h2
-rw-r--r--src/widgets/widgets/qscrollbar.cpp28
-rw-r--r--src/widgets/widgets/qsizegrip.cpp6
-rw-r--r--src/widgets/widgets/qspinbox.cpp7
-rw-r--r--src/widgets/widgets/qsplashscreen.cpp54
-rw-r--r--src/widgets/widgets/qsplitter.cpp2
-rw-r--r--src/widgets/widgets/qsplitter_p.h2
-rw-r--r--src/widgets/widgets/qstackedwidget.cpp6
-rw-r--r--src/widgets/widgets/qstatusbar.cpp34
-rw-r--r--src/widgets/widgets/qtabbar.cpp94
-rw-r--r--src/widgets/widgets/qtabbar.h2
-rw-r--r--src/widgets/widgets/qtabbar_p.h12
-rw-r--r--src/widgets/widgets/qtabwidget.cpp50
-rw-r--r--src/widgets/widgets/qtabwidget.h3
-rw-r--r--src/widgets/widgets/qtextbrowser.cpp29
-rw-r--r--src/widgets/widgets/qtextbrowser.h3
-rw-r--r--src/widgets/widgets/qtextedit.cpp156
-rw-r--r--src/widgets/widgets/qtextedit.h8
-rw-r--r--src/widgets/widgets/qtextedit_p.h19
-rw-r--r--src/widgets/widgets/qtoolbar.cpp52
-rw-r--r--src/widgets/widgets/qtoolbarextension.cpp6
-rw-r--r--src/widgets/widgets/qtoolbutton.cpp60
-rw-r--r--src/widgets/widgets/qtoolbutton.h8
-rw-r--r--src/widgets/widgets/qwidgetanimator.cpp3
-rw-r--r--src/widgets/widgets/qwidgetlinecontrol.cpp12
-rw-r--r--src/widgets/widgets/qwidgetlinecontrol_p.h2
-rw-r--r--src/widgets/widgets/qwidgettextcontrol.cpp12
-rw-r--r--src/widgets/widgets/qwidgettextcontrol_p.h5
87 files changed, 2594 insertions, 1440 deletions
diff --git a/src/widgets/widgets/qabstractbutton.cpp b/src/widgets/widgets/qabstractbutton.cpp
index 218ec88f53..3a5fbe1b90 100644
--- a/src/widgets/widgets/qabstractbutton.cpp
+++ b/src/widgets/widgets/qabstractbutton.cpp
@@ -21,6 +21,8 @@
#endif
#include <qpa/qplatformtheme.h>
+#include <QtCore/qpointer.h>
+
#include <algorithm>
QT_BEGIN_NAMESPACE
@@ -613,12 +615,13 @@ void QAbstractButton::setChecked(bool checked)
if (guard)
d->emitToggled(checked);
-
#if QT_CONFIG(accessibility)
- QAccessible::State s;
- s.checked = true;
- QAccessibleStateChangeEvent event(this, s);
- QAccessible::updateAccessibility(&event);
+ if (guard) {
+ QAccessible::State s;
+ s.checked = true;
+ QAccessibleStateChangeEvent event(this, s);
+ QAccessible::updateAccessibility(&event);
+ }
#endif
}
diff --git a/src/widgets/widgets/qabstractscrollarea.cpp b/src/widgets/widgets/qabstractscrollarea.cpp
index 91fedea650..f6f8e8b795 100644
--- a/src/widgets/widgets/qabstractscrollarea.cpp
+++ b/src/widgets/widgets/qabstractscrollarea.cpp
@@ -295,22 +295,25 @@ void QAbstractScrollAreaPrivate::layoutChildren()
void QAbstractScrollAreaPrivate::layoutChildren_helper(bool *needHorizontalScrollbar, bool *needVerticalScrollbar)
{
Q_Q(QAbstractScrollArea);
- bool htransient = hbar->style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, hbar);
+ QStyleOptionSlider barOpt;
+
+ hbar->initStyleOption(&barOpt);
+ bool htransient = hbar->style()->styleHint(QStyle::SH_ScrollBar_Transient, &barOpt, hbar);
bool needh = *needHorizontalScrollbar || ((hbarpolicy != Qt::ScrollBarAlwaysOff) && ((hbarpolicy == Qt::ScrollBarAlwaysOn && !htransient)
|| ((hbarpolicy == Qt::ScrollBarAsNeeded || htransient)
&& hbar->minimum() < hbar->maximum() && !hbar->sizeHint().isEmpty())));
+ const int hscrollOverlap = hbar->style()->pixelMetric(QStyle::PM_ScrollView_ScrollBarOverlap, &barOpt, hbar);
- bool vtransient = vbar->style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, vbar);
+ vbar->initStyleOption(&barOpt);
+ bool vtransient = vbar->style()->styleHint(QStyle::SH_ScrollBar_Transient, &barOpt, vbar);
bool needv = *needVerticalScrollbar || ((vbarpolicy != Qt::ScrollBarAlwaysOff) && ((vbarpolicy == Qt::ScrollBarAlwaysOn && !vtransient)
|| ((vbarpolicy == Qt::ScrollBarAsNeeded || vtransient)
&& vbar->minimum() < vbar->maximum() && !vbar->sizeHint().isEmpty())));
+ const int vscrollOverlap = vbar->style()->pixelMetric(QStyle::PM_ScrollView_ScrollBarOverlap, &barOpt, vbar);
QStyleOption opt(0);
opt.initFrom(q);
- const int hscrollOverlap = hbar->style()->pixelMetric(QStyle::PM_ScrollView_ScrollBarOverlap, &opt, hbar);
- const int vscrollOverlap = vbar->style()->pixelMetric(QStyle::PM_ScrollView_ScrollBarOverlap, &opt, vbar);
-
const int hsbExt = hbar->sizeHint().height();
const int vsbExt = vbar->sizeHint().width();
const QPoint extPoint(vsbExt, hsbExt);
@@ -532,15 +535,13 @@ scrolling range.
QSize QAbstractScrollArea::maximumViewportSize() const
{
Q_D(const QAbstractScrollArea);
- int hsbExt = d->hbar->sizeHint().height();
- int vsbExt = d->vbar->sizeHint().width();
-
int f = 2 * d->frameWidth;
QSize max = size() - QSize(f + d->left + d->right, f + d->top + d->bottom);
+ // Count the sizeHint of the bar only if it is displayed.
if (d->vbarpolicy == Qt::ScrollBarAlwaysOn)
- max.rwidth() -= vsbExt;
+ max.rwidth() -= d->vbar->sizeHint().width();
if (d->hbarpolicy == Qt::ScrollBarAlwaysOn)
- max.rheight() -= hsbExt;
+ max.rheight() -= d->hbar->sizeHint().height();
return max;
}
@@ -1099,7 +1100,7 @@ void QAbstractScrollArea::resizeEvent(QResizeEvent *)
This event handler can be reimplemented in a subclass to receive
paint events (passed in \a event), for the viewport() widget.
- \note If you open a painter, make sure to open it on the viewport().
+ \note If you create a QPainter, it must operate on the viewport().
\sa QWidget::paintEvent()
*/
@@ -1342,10 +1343,14 @@ bool QAbstractScrollAreaPrivate::canStartScrollingAt(const QPoint &startPos) con
void QAbstractScrollAreaPrivate::flashScrollBars()
{
- bool htransient = hbar->style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, hbar);
+ QStyleOptionSlider opt;
+ hbar->initStyleOption(&opt);
+
+ bool htransient = hbar->style()->styleHint(QStyle::SH_ScrollBar_Transient, &opt, hbar);
if ((hbarpolicy != Qt::ScrollBarAlwaysOff) && (hbarpolicy == Qt::ScrollBarAsNeeded || htransient))
hbar->d_func()->flash();
- bool vtransient = vbar->style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, vbar);
+ vbar->initStyleOption(&opt);
+ bool vtransient = vbar->style()->styleHint(QStyle::SH_ScrollBar_Transient, &opt, vbar);
if ((vbarpolicy != Qt::ScrollBarAlwaysOff) && (vbarpolicy == Qt::ScrollBarAsNeeded || vtransient))
vbar->d_func()->flash();
}
@@ -1426,9 +1431,9 @@ QSize QAbstractScrollArea::sizeHint() const
if (!d->sizeHint.isValid() || d->sizeAdjustPolicy == QAbstractScrollArea::AdjustToContents) {
const int f = 2 * d->frameWidth;
- const QSize frame( f, f );
- const bool vbarHidden = d->vbar->isHidden() || d->vbarpolicy == Qt::ScrollBarAlwaysOff;
- const bool hbarHidden = d->hbar->isHidden() || d->hbarpolicy == Qt::ScrollBarAlwaysOff;
+ const QSize frame(f, f);
+ const bool vbarHidden = !d->vbar->isVisibleTo(this) || d->vbarpolicy == Qt::ScrollBarAlwaysOff;
+ const bool hbarHidden = !d->vbar->isVisibleTo(this) || d->hbarpolicy == Qt::ScrollBarAlwaysOff;
const QSize scrollbars(vbarHidden ? 0 : d->vbar->sizeHint().width(),
hbarHidden ? 0 : d->hbar->sizeHint().height());
d->sizeHint = frame + scrollbars + viewportSizeHint();
diff --git a/src/widgets/widgets/qabstractslider.cpp b/src/widgets/widgets/qabstractslider.cpp
index 6e44b2d432..4e65fbac88 100644
--- a/src/widgets/widgets/qabstractslider.cpp
+++ b/src/widgets/widgets/qabstractslider.cpp
@@ -498,18 +498,25 @@ void QAbstractSlider::setValue(int value)
value = d->bound(value);
if (d->value == value && d->position == value)
return;
+
+ // delay signal emission until sliderChanged() has been called
+ const bool emitValueChanged = (value != d->value);
d->value = value;
+
if (d->position != value) {
d->position = value;
if (d->pressed)
- emit sliderMoved((d->position = value));
+ emit sliderMoved(d->position);
}
#if QT_CONFIG(accessibility)
QAccessibleValueChangeEvent event(this, d->value);
QAccessible::updateAccessibility(&event);
#endif
sliderChange(SliderValueChange);
- emit valueChanged(value);
+
+ if (emitValueChanged)
+ emit valueChanged(value);
+
}
/*!
diff --git a/src/widgets/widgets/qabstractspinbox.cpp b/src/widgets/widgets/qabstractspinbox.cpp
index 10aea1da3c..f377275d23 100644
--- a/src/widgets/widgets/qabstractspinbox.cpp
+++ b/src/widgets/widgets/qabstractspinbox.cpp
@@ -26,6 +26,7 @@
# include <qaccessible.h>
#endif
+#include <QtCore/qpointer.h>
//#define QABSTRACTSPINBOX_QSBDEBUG
#ifdef QABSTRACTSPINBOX_QSBDEBUG
@@ -691,14 +692,14 @@ void QAbstractSpinBox::setLineEdit(QLineEdit *lineEdit)
d->edit->setAcceptDrops(false);
if (d->type != QMetaType::UnknownType) {
- connect(d->edit, SIGNAL(textChanged(QString)),
- this, SLOT(_q_editorTextChanged(QString)));
- connect(d->edit, SIGNAL(cursorPositionChanged(int,int)),
- this, SLOT(_q_editorCursorPositionChanged(int,int)));
- connect(d->edit, SIGNAL(cursorPositionChanged(int,int)),
- this, SLOT(updateMicroFocus()));
- connect(d->edit->d_func()->control, SIGNAL(updateMicroFocus()),
- this, SLOT(updateMicroFocus()));
+ QObjectPrivate::connect(d->edit, &QLineEdit::textChanged,
+ d, &QAbstractSpinBoxPrivate::editorTextChanged);
+ QObjectPrivate::connect(d->edit, &QLineEdit::cursorPositionChanged,
+ d, &QAbstractSpinBoxPrivate::editorCursorPositionChanged);
+ connect(d->edit, &QLineEdit::cursorPositionChanged,
+ this, [this]() { updateMicroFocus(); });
+ connect(d->edit->d_func()->control, &QWidgetLineControl::updateMicroFocus,
+ this, [this]() { updateMicroFocus(); });
}
d->updateEditFieldGeometry();
d->edit->setContextMenuPolicy(Qt::NoContextMenu);
@@ -1510,7 +1511,7 @@ void QAbstractSpinBoxPrivate::emitSignals(EmitPolicy, const QVariant &)
signal.
*/
-void QAbstractSpinBoxPrivate::_q_editorTextChanged(const QString &t)
+void QAbstractSpinBoxPrivate::editorTextChanged(const QString &t)
{
Q_Q(QAbstractSpinBox);
@@ -1540,7 +1541,7 @@ void QAbstractSpinBoxPrivate::_q_editorTextChanged(const QString &t)
the different sections etc.
*/
-void QAbstractSpinBoxPrivate::_q_editorCursorPositionChanged(int oldpos, int newpos)
+void QAbstractSpinBoxPrivate::editorCursorPositionChanged(int oldpos, int newpos)
{
if (!edit->hasSelectedText() && !ignoreCursorPositionChanged && !specialValue()) {
ignoreCursorPositionChanged = true;
@@ -2042,6 +2043,7 @@ QVariant operator-(const QVariant &arg1, const QVariant &arg2)
dt.setTime(dt.time().addMSecs(msecs));
ret = QVariant(dt);
}
+ break;
}
default: break;
}
diff --git a/src/widgets/widgets/qabstractspinbox.h b/src/widgets/widgets/qabstractspinbox.h
index 6707db45c8..91d3dcb8fc 100644
--- a/src/widgets/widgets/qabstractspinbox.h
+++ b/src/widgets/widgets/qabstractspinbox.h
@@ -136,9 +136,6 @@ protected:
QAbstractSpinBox(QAbstractSpinBoxPrivate &dd, QWidget *parent = nullptr);
private:
- Q_PRIVATE_SLOT(d_func(), void _q_editorTextChanged(const QString &))
- Q_PRIVATE_SLOT(d_func(), void _q_editorCursorPositionChanged(int, int))
-
Q_DECLARE_PRIVATE(QAbstractSpinBox)
Q_DISABLE_COPY(QAbstractSpinBox)
friend class QAccessibleAbstractSpinBox;
diff --git a/src/widgets/widgets/qabstractspinbox_p.h b/src/widgets/widgets/qabstractspinbox_p.h
index efc23586cf..4252a01423 100644
--- a/src/widgets/widgets/qabstractspinbox_p.h
+++ b/src/widgets/widgets/qabstractspinbox_p.h
@@ -74,8 +74,8 @@ public:
virtual QString textFromValue(const QVariant &n) const;
virtual QVariant valueFromText(const QString &input) const;
- void _q_editorTextChanged(const QString &);
- virtual void _q_editorCursorPositionChanged(int oldpos, int newpos);
+ void editorTextChanged(const QString &);
+ virtual void editorCursorPositionChanged(int oldpos, int newpos);
virtual QStyle::SubControl newHoverControl(const QPoint &pos);
bool updateHoverControl(const QPoint &pos);
diff --git a/src/widgets/widgets/qbuttongroup.cpp b/src/widgets/widgets/qbuttongroup.cpp
index 3a992fed74..079ef45e90 100644
--- a/src/widgets/widgets/qbuttongroup.cpp
+++ b/src/widgets/widgets/qbuttongroup.cpp
@@ -57,8 +57,8 @@ void QButtonGroupPrivate::detectCheckedButton()
In addition, QButtonGroup can map between integers and buttons.
You can assign an integer id to a button with setId(), and
retrieve it with id(). The id of the currently checked button is
- available with checkedId(), and there is an overloaded signal
- buttonClicked() which emits the id of the button. The id \c {-1}
+ available with checkedId(), and there is a signal
+ idClicked() that emits the id of the button. The id \c {-1}
is reserved by QButtonGroup to mean "no such button". The purpose
of the mapping mechanism is to simplify the representation of enum
values in a user interface.
diff --git a/src/widgets/widgets/qcalendarwidget.cpp b/src/widgets/widgets/qcalendarwidget.cpp
index 8570232d23..034127b4f3 100644
--- a/src/widgets/widgets/qcalendarwidget.cpp
+++ b/src/widgets/widgets/qcalendarwidget.cpp
@@ -2200,7 +2200,7 @@ QSize QCalendarWidget::minimumSizeHint() const
QStyleOption option;
option.initFrom(this);
- const int marginH = (style()->pixelMetric(QStyle::PM_FocusFrameHMargin, &option) + 1) * 2;
+ const int marginH = (style()->pixelMetric(QStyle::PM_FocusFrameHMargin, &option, this) + 1) * 2;
if (horizontalHeaderFormat() == QCalendarWidget::NoHorizontalHeader) {
rows = 6;
@@ -2492,14 +2492,14 @@ void QCalendarWidget::showToday()
\snippet code/src_gui_widgets_qcalendarwidget.cpp 1
\endtable
- By default, the minimum date is the earliest date that the QDate
- class can handle.
-
When setting a minimum date, the maximumDate and selectedDate
properties are adjusted if the selection range becomes invalid. If
the provided date is not a valid QDate object, the
setMinimumDate() function does nothing.
+ The default minimum date is November 25, 4714 BCE.
+ You can restore this default by calling clearMinimumDate() (since Qt 6.6).
+
\sa setDateRange()
*/
@@ -2548,14 +2548,14 @@ void QCalendarWidget::clearMinimumDate()
\snippet code/src_gui_widgets_qcalendarwidget.cpp 2
\endtable
- By default, the maximum date is the last day the QDate class can
- handle.
-
When setting a maximum date, the minimumDate and selectedDate
properties are adjusted if the selection range becomes invalid. If
the provided date is not a valid QDate object, the
setMaximumDate() function does nothing.
+ The default maximum date is December 31, 9999 CE.
+ You can restore this default by calling clearMaximumDate() (since Qt 6.6).
+
\sa setDateRange()
*/
@@ -2873,7 +2873,7 @@ QMap<QDate, QTextCharFormat> QCalendarWidget::dateTextFormat() const
}
/*!
- Returns a QTextCharFormat for \a date. The char format can be be
+ Returns a QTextCharFormat for \a date. The char format can be
empty if the date is not renderd specially.
*/
QTextCharFormat QCalendarWidget::dateTextFormat(QDate date) const
@@ -3099,6 +3099,7 @@ bool QCalendarWidget::event(QEvent *event)
case QEvent::StyleChange:
d->cachedSizeHint = QSize();
d->m_view->updateGeometry();
+ break;
default:
break;
}
diff --git a/src/widgets/widgets/qcheckbox.cpp b/src/widgets/widgets/qcheckbox.cpp
index 151c8b1e94..88cd603d70 100644
--- a/src/widgets/widgets/qcheckbox.cpp
+++ b/src/widgets/widgets/qcheckbox.cpp
@@ -28,7 +28,7 @@ public:
uint tristate : 1;
uint noChange : 1;
uint hovering : 1;
- uint publishedState : 2;
+ Qt::CheckState publishedState : 3;
void init();
};
@@ -59,7 +59,7 @@ public:
\endtable
Whenever a checkbox is checked or cleared, it emits the signal
- stateChanged(). Connect to this signal if you want to trigger an action
+ checkStateChanged(). Connect to this signal if you want to trigger an action
each time the checkbox changes state. You can use isChecked() to query
whether or not a checkbox is checked.
@@ -84,7 +84,7 @@ public:
setPixmap(), accel(), setAccel(), isToggleButton(), setDown(), isDown(),
isOn(), checkState(), autoRepeat(), isExclusiveToggle(), group(),
setAutoRepeat(), toggle(), pressed(), released(), clicked(), toggled(),
- checkState(), and stateChanged().
+ checkState(), and checkStateChanged().
\sa QAbstractButton, QRadioButton
*/
@@ -92,6 +92,13 @@ public:
/*!
\fn void QCheckBox::stateChanged(int state)
+ \deprecated [6.9] Use checkStateChanged(Qt::CheckState) instead.
+*/
+
+/*!
+ \fn void QCheckBox::checkStateChanged(Qt::CheckState state)
+ \since 6.7
+
This signal is emitted whenever the checkbox's state changes, i.e.,
whenever the user checks or unchecks it.
@@ -224,9 +231,14 @@ void QCheckBox::setCheckState(Qt::CheckState state)
setChecked(state != Qt::Unchecked);
d->blockRefresh = false;
d->refresh();
- if ((uint)state != d->publishedState) {
+ if (state != d->publishedState) {
d->publishedState = state;
+ emit checkStateChanged(state);
+#if QT_DEPRECATED_SINCE(6, 9)
+ QT_IGNORE_DEPRECATIONS(
emit stateChanged(state);
+ )
+#endif
}
#if QT_CONFIG(accessibility)
@@ -319,9 +331,14 @@ void QCheckBox::checkStateSet()
Q_D(QCheckBox);
d->noChange = false;
Qt::CheckState state = checkState();
- if ((uint)state != d->publishedState) {
+ if (state != d->publishedState) {
d->publishedState = state;
+ emit checkStateChanged(state);
+#if QT_DEPRECATED_SINCE(6, 9)
+ QT_IGNORE_DEPRECATIONS(
emit stateChanged(state);
+ )
+#endif
}
}
diff --git a/src/widgets/widgets/qcheckbox.h b/src/widgets/widgets/qcheckbox.h
index ab240b5500..de623b7b9e 100644
--- a/src/widgets/widgets/qcheckbox.h
+++ b/src/widgets/widgets/qcheckbox.h
@@ -36,7 +36,11 @@ public:
void setCheckState(Qt::CheckState state);
Q_SIGNALS:
+#if QT_DEPRECATED_SINCE(6, 9)
+ QT_MOC_COMPAT QT_DEPRECATED_VERSION_X_6_9("Use checkStateChanged() instead")
void stateChanged(int);
+#endif
+ void checkStateChanged(Qt::CheckState);
protected:
bool event(QEvent *e) override;
diff --git a/src/widgets/widgets/qcombobox.cpp b/src/widgets/widgets/qcombobox.cpp
index 7bfce05d68..1fe9a8d7be 100644
--- a/src/widgets/widgets/qcombobox.cpp
+++ b/src/widgets/widgets/qcombobox.cpp
@@ -12,7 +12,7 @@
#if QT_CONFIG(tableview)
#include <qtableview.h>
#endif
-#include <qitemdelegate.h>
+#include <qabstractitemdelegate.h>
#include <qmap.h>
#if QT_CONFIG(menu)
#include <qmenu.h>
@@ -48,6 +48,9 @@
#if QT_CONFIG(accessibility)
#include "qaccessible.h"
#endif
+#include <array>
+
+#include <QtCore/qpointer.h>
QT_BEGIN_NAMESPACE
@@ -65,6 +68,7 @@ QComboBoxPrivate::QComboBoxPrivate()
QComboBoxPrivate::~QComboBoxPrivate()
{
+ disconnectModel();
#ifdef Q_OS_MAC
cleanupNativePopup();
#endif
@@ -200,7 +204,7 @@ bool QComboMenuDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
}
#if QT_CONFIG(completer)
-void QComboBoxPrivate::_q_completerActivated(const QModelIndex &index)
+void QComboBoxPrivate::completerActivated(const QModelIndex &index)
{
Q_Q(QComboBox);
#if QT_CONFIG(proxymodel)
@@ -240,7 +244,7 @@ void QComboBoxPrivate::updateArrow(QStyle::StateFlag state)
q->update(q->rect());
}
-void QComboBoxPrivate::_q_modelReset()
+void QComboBoxPrivate::modelReset()
{
Q_Q(QComboBox);
if (lineEdit) {
@@ -252,7 +256,7 @@ void QComboBoxPrivate::_q_modelReset()
q->update();
}
-void QComboBoxPrivate::_q_modelDestroyed()
+void QComboBoxPrivate::modelDestroyed()
{
model = QAbstractItemModelPrivate::staticEmptyModel();
}
@@ -490,11 +494,13 @@ QComboBoxPrivateContainer::QComboBoxPrivateContainer(QAbstractItemView *itemView
if (top) {
layout->insertWidget(0, top);
- connect(top, SIGNAL(doScroll(int)), this, SLOT(scrollItemView(int)));
+ connect(top, &QComboBoxPrivateScroller::doScroll,
+ this, &QComboBoxPrivateContainer::scrollItemView);
}
if (bottom) {
layout->addWidget(bottom);
- connect(bottom, SIGNAL(doScroll(int)), this, SLOT(scrollItemView(int)));
+ connect(bottom, &QComboBoxPrivateScroller::doScroll,
+ this, &QComboBoxPrivateContainer::scrollItemView);
}
// Some styles (Mac) have a margin at the top and bottom of the popup.
@@ -503,6 +509,12 @@ QComboBoxPrivateContainer::QComboBoxPrivateContainer(QAbstractItemView *itemView
updateStyleSettings();
}
+QComboBoxPrivateContainer::~QComboBoxPrivateContainer()
+{
+ disconnect(view, &QAbstractItemView::destroyed,
+ this, &QComboBoxPrivateContainer::viewDestroyed);
+}
+
void QComboBoxPrivateContainer::scrollItemView(int action)
{
#if QT_CONFIG(scrollbar)
@@ -583,13 +595,13 @@ void QComboBoxPrivateContainer::setItemView(QAbstractItemView *itemView)
view->removeEventFilter(this);
view->viewport()->removeEventFilter(this);
#if QT_CONFIG(scrollbar)
- disconnect(view->verticalScrollBar(), SIGNAL(valueChanged(int)),
- this, SLOT(updateScrollers()));
- disconnect(view->verticalScrollBar(), SIGNAL(rangeChanged(int,int)),
- this, SLOT(updateScrollers()));
+ disconnect(view->verticalScrollBar(), &QScrollBar::valueChanged,
+ this, &QComboBoxPrivateContainer::updateScrollers);
+ disconnect(view->verticalScrollBar(), &QScrollBar::rangeChanged,
+ this, &QComboBoxPrivateContainer::updateScrollers);
#endif
- disconnect(view, SIGNAL(destroyed()),
- this, SLOT(viewDestroyed()));
+ disconnect(view, &QAbstractItemView::destroyed,
+ this, &QComboBoxPrivateContainer::viewDestroyed);
if (isAncestorOf(view))
delete view;
@@ -620,13 +632,13 @@ void QComboBoxPrivateContainer::setItemView(QAbstractItemView *itemView)
view->setLineWidth(0);
view->setEditTriggers(QAbstractItemView::NoEditTriggers);
#if QT_CONFIG(scrollbar)
- connect(view->verticalScrollBar(), SIGNAL(valueChanged(int)),
- this, SLOT(updateScrollers()));
- connect(view->verticalScrollBar(), SIGNAL(rangeChanged(int,int)),
- this, SLOT(updateScrollers()));
+ connect(view->verticalScrollBar(), &QScrollBar::valueChanged,
+ this, &QComboBoxPrivateContainer::updateScrollers);
+ connect(view->verticalScrollBar(), &QScrollBar::rangeChanged,
+ this, &QComboBoxPrivateContainer::updateScrollers);
#endif
- connect(view, SIGNAL(destroyed()),
- this, SLOT(viewDestroyed()));
+ connect(view, &QAbstractItemView::destroyed,
+ this, &QComboBoxPrivateContainer::viewDestroyed);
}
/*!
@@ -715,6 +727,7 @@ bool QComboBoxPrivateContainer::eventFilter(QObject *o, QEvent *e)
#endif
if (view->currentIndex().isValid() && view->currentIndex().flags().testFlag(Qt::ItemIsEnabled)) {
combo->hidePopup();
+ keyEvent->accept();
emit itemSelected(view->currentIndex());
}
return true;
@@ -724,6 +737,8 @@ bool QComboBoxPrivateContainer::eventFilter(QObject *o, QEvent *e)
Q_FALLTHROUGH();
case Qt::Key_F4:
combo->hidePopup();
+ keyEvent->accept();
+ emit itemSelected(view->currentIndex());
return true;
default:
#if QT_CONFIG(shortcut)
@@ -904,8 +919,11 @@ QStyleOptionComboBox QComboBoxPrivateContainer::comboStyleOption() const
\fn void QComboBox::currentTextChanged(const QString &text)
\since 5.0
- This signal is sent whenever currentText changes. The new value
- is passed as \a text.
+ This signal is emitted whenever currentText changes.
+ The new value is passed as \a text.
+
+ \note It is not emitted, if currentText remains the same,
+ even if currentIndex changes.
*/
/*!
@@ -931,39 +949,34 @@ QComboBox::QComboBox(QComboBoxPrivate &dd, QWidget *parent)
/*!
\class QComboBox
- \brief The QComboBox widget is a combined button and popup list.
+ \brief The QComboBox widget combines a button with a dropdown list.
\ingroup basicwidgets
\inmodule QtWidgets
- \image windows-combobox.png
-
- A QComboBox provides a means of presenting a list of options to the user
- in a way that takes up the minimum amount of screen space.
-
- A combobox is a selection widget that displays the current item,
- and can pop up a list of selectable items. A combobox may be editable,
- allowing the user to modify each item in the list.
-
- Comboboxes can contain pixmaps as well as strings; the
- insertItem() and setItemText() functions are suitably overloaded.
- For editable comboboxes, the function clearEditText() is provided,
+ \table
+ \row
+ \li \inlineimage collapsed_combobox.png
+ \caption Collapsed QCombobox
+ \li
+ \inlineimage expanded_combobox.png
+ \caption Expanded QCombobox
+ \endtable
+
+ \section1 Display Features
+ A QComboBox is a compact way to present a list of options to the user.
+
+ A combobox is a selection widget that shows the current item,
+ and pops up a list of selectable items when clicked. Comboboxes can
+ contain pixmaps as well as strings if the insertItem() and setItemText()
+ functions are suitably overloaded.
+
+ \section1 Editing Features
+ A combobox may be editable, allowing the user to modify each item in the
+ list. For editable comboboxes, the function clearEditText() is provided,
to clear the displayed string without changing the combobox's
contents.
- There are three signals emitted if the current item of a combobox
- changes, currentIndexChanged(), currentTextChanged() and activated().
- currentIndexChanged() and currentTextChanged() are always emitted
- regardless if the change
- was done programmatically or by user interaction, while
- activated() is only emitted when the change is caused by user
- interaction. The highlighted() signal is emitted when the user
- highlights an item in the combobox popup list. All three signals
- exist in two versions, one with a QString argument and one with an
- \c int argument. If the user selects or highlights a pixmap, only
- the \c int signals are emitted. Whenever the text of an editable
- combobox is changed the editTextChanged() signal is emitted.
-
When the user enters a new string in an editable combobox, the
widget may or may not insert it, and it can insert it in several
locations. The default policy is \l InsertAtBottom but you can change
@@ -986,17 +999,33 @@ QComboBox::QComboBox(QComboBoxPrivate &dd, QWidget *parent)
setCompleter() and whether or not the user can add duplicates
is set with setDuplicatesEnabled().
- QComboBox uses the \l{Model/View Programming}{model/view
- framework} for its popup list and to store its items. By default
- a QStandardItemModel stores the items and a QListView subclass
- displays the popuplist. You can access the model and view directly
- (with model() and view()), but QComboBox also provides functions
- to set and get item data (e.g., setItemData() and itemText()). You
- can also set a new model and view (with setModel() and setView()).
- For the text and icon in the combobox label, the data in the model
- that has the Qt::DisplayRole and Qt::DecorationRole is used. Note
- that you cannot alter the \l{QAbstractItemView::}{SelectionMode}
- of the view(), e.g., by using
+ \section1 Signals
+ There are three signals emitted if the current item of a combobox
+ changes: currentIndexChanged(), currentTextChanged(), and activated().
+ currentIndexChanged() and currentTextChanged() are always emitted
+ regardless if the change
+ was done programmatically or by user interaction, while
+ activated() is only emitted when the change is caused by user
+ interaction. The highlighted() signal is emitted when the user
+ highlights an item in the combobox popup list. All three signals
+ exist in two versions, one with a QString argument and one with an
+ \c int argument. If the user selects or highlights a pixmap, only
+ the \c int signals are emitted. Whenever the text of an editable
+ combobox is changed, the editTextChanged() signal is emitted.
+
+ \section1 Model/View Framework
+
+ QComboBox uses the \l{Model/View Programming}{model/view framework} for its
+ popup list and to store its items. By default a QStandardItemModel stores
+ the items and a QListView subclass displays the popuplist. You can access
+ the model and view directly (with model() and view()), but QComboBox also
+ provides functions to set and get item data, for example, setItemData() and
+ itemText(). You can also set a new model and view (with setModel()
+ and setView()). For the text and icon in the combobox label, the data in
+ the model that has the Qt::DisplayRole and Qt::DecorationRole is used.
+
+ \note You cannot alter the \l{QAbstractItemView::}{SelectionMode}
+ of the view(), for example, by using
\l{QAbstractItemView::}{setSelectionMode()}.
\sa QLineEdit, QSpinBox, QRadioButton, QButtonGroup
@@ -1033,27 +1062,30 @@ QComboBoxPrivateContainer* QComboBoxPrivate::viewContainer()
Q_Q(QComboBox);
container = new QComboBoxPrivateContainer(new QComboBoxListView(q), q);
+ disconnectModel();
container->itemView()->setModel(model);
+ connectModel();
container->itemView()->setTextElideMode(Qt::ElideMiddle);
updateDelegate(true);
updateLayoutDirection();
updateViewContainerPaletteAndOpacity();
- QObject::connect(container, SIGNAL(itemSelected(QModelIndex)),
- q, SLOT(_q_itemSelected(QModelIndex)));
- QObject::connect(container->itemView()->selectionModel(),
- SIGNAL(currentChanged(QModelIndex,QModelIndex)),
- q, SLOT(_q_emitHighlighted(QModelIndex)));
- QObject::connect(container, SIGNAL(resetButton()), q, SLOT(_q_resetButton()));
+ QObjectPrivate::connect(container, &QComboBoxPrivateContainer::itemSelected,
+ this, &QComboBoxPrivate::itemSelected);
+ QObjectPrivate::connect(container->itemView()->selectionModel(),
+ &QItemSelectionModel::currentChanged,
+ this, &QComboBoxPrivate::emitHighlighted);
+ QObjectPrivate::connect(container, &QComboBoxPrivateContainer::resetButton,
+ this, &QComboBoxPrivate::resetButton);
return container;
}
-void QComboBoxPrivate::_q_resetButton()
+void QComboBoxPrivate::resetButton()
{
updateArrow(QStyle::State_None);
}
-void QComboBoxPrivate::_q_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
+void QComboBoxPrivate::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
{
Q_Q(QComboBox);
if (inserting || topLeft.parent() != root)
@@ -1071,7 +1103,7 @@ void QComboBoxPrivate::_q_dataChanged(const QModelIndex &topLeft, const QModelIn
lineEdit->setText(text);
updateLineEditGeometry();
} else {
- emit q->currentTextChanged(text);
+ updateCurrentText(text);
}
q->update();
#if QT_CONFIG(accessibility)
@@ -1081,7 +1113,7 @@ void QComboBoxPrivate::_q_dataChanged(const QModelIndex &topLeft, const QModelIn
}
}
-void QComboBoxPrivate::_q_rowsInserted(const QModelIndex &parent, int start, int end)
+void QComboBoxPrivate::rowsInserted(const QModelIndex &parent, int start, int end)
{
Q_Q(QComboBox);
if (inserting || parent != root)
@@ -1096,20 +1128,31 @@ void QComboBoxPrivate::_q_rowsInserted(const QModelIndex &parent, int start, int
// set current index if combo was previously empty and there is no placeholderText
if (start == 0 && (end - start + 1) == q->count() && !currentIndex.isValid() &&
placeholderText.isEmpty()) {
+#if QT_CONFIG(accessibility)
+ // This might have been called by the model emitting rowInserted(), at which
+ // point the view won't have updated the accessibility bridge yet about its new
+ // dimensions. Do it now so that the change of the selection matches the row
+ // indexes of the accessibility bridge's representation.
+ if (container && container->itemView()) {
+ QAccessibleTableModelChangeEvent event(container->itemView(),
+ QAccessibleTableModelChangeEvent::ModelReset);
+ QAccessible::updateAccessibility(&event);
+ }
+#endif
q->setCurrentIndex(0);
// need to emit changed if model updated index "silently"
} else if (currentIndex.row() != indexBeforeChange) {
q->update();
- _q_emitCurrentIndexChanged(currentIndex);
+ emitCurrentIndexChanged(currentIndex);
}
}
-void QComboBoxPrivate::_q_updateIndexBeforeChange()
+void QComboBoxPrivate::updateIndexBeforeChange()
{
indexBeforeChange = currentIndex.row();
}
-void QComboBoxPrivate::_q_rowsRemoved(const QModelIndex &parent, int /*start*/, int /*end*/)
+void QComboBoxPrivate::rowsRemoved(const QModelIndex &parent, int /*start*/, int /*end*/)
{
Q_Q(QComboBox);
if (parent != root)
@@ -1121,6 +1164,12 @@ void QComboBoxPrivate::_q_rowsRemoved(const QModelIndex &parent, int /*start*/,
q->updateGeometry();
}
+ // model has removed the last row
+ if (model->rowCount(root) == 0) {
+ setCurrentIndex(QModelIndex());
+ return;
+ }
+
// model has changed the currentIndex
if (currentIndex.row() != indexBeforeChange) {
if (!currentIndex.isValid() && q->count()) {
@@ -1132,7 +1181,7 @@ void QComboBoxPrivate::_q_rowsRemoved(const QModelIndex &parent, int /*start*/,
updateLineEditGeometry();
}
q->update();
- _q_emitCurrentIndexChanged(currentIndex);
+ emitCurrentIndexChanged(currentIndex);
}
}
@@ -1241,7 +1290,7 @@ Qt::MatchFlags QComboBoxPrivate::matchFlags() const
}
-void QComboBoxPrivate::_q_editingFinished()
+void QComboBoxPrivate::editingFinished()
{
Q_Q(QComboBox);
if (!lineEdit)
@@ -1273,13 +1322,13 @@ void QComboBoxPrivate::_q_editingFinished()
}
-void QComboBoxPrivate::_q_returnPressed()
+void QComboBoxPrivate::returnPressed()
{
Q_Q(QComboBox);
// The insertion code below does not apply when the policy is QComboBox::NoInsert.
// In case a completer is installed, item activation via the completer is handled
- // in _q_completerActivated(). Otherwise _q_editingFinished() updates the current
+ // in completerActivated(). Otherwise editingFinished() updates the current
// index as appropriate.
if (insertPolicy == QComboBox::NoInsert)
return;
@@ -1337,7 +1386,7 @@ void QComboBoxPrivate::_q_returnPressed()
}
}
-void QComboBoxPrivate::_q_itemSelected(const QModelIndex &item)
+void QComboBoxPrivate::itemSelected(const QModelIndex &item)
{
Q_Q(QComboBox);
if (item != currentIndex) {
@@ -1359,7 +1408,7 @@ void QComboBoxPrivate::emitActivated(const QModelIndex &index)
emit q->textActivated(text);
}
-void QComboBoxPrivate::_q_emitHighlighted(const QModelIndex &index)
+void QComboBoxPrivate::emitHighlighted(const QModelIndex &index)
{
Q_Q(QComboBox);
if (!index.isValid())
@@ -1369,14 +1418,14 @@ void QComboBoxPrivate::_q_emitHighlighted(const QModelIndex &index)
emit q->textHighlighted(text);
}
-void QComboBoxPrivate::_q_emitCurrentIndexChanged(const QModelIndex &index)
+void QComboBoxPrivate::emitCurrentIndexChanged(const QModelIndex &index)
{
Q_Q(QComboBox);
const QString text = itemText(index);
emit q->currentIndexChanged(index.row());
// signal lineEdit.textChanged already connected to signal currentTextChanged, so don't emit double here
if (!lineEdit)
- emit q->currentTextChanged(text);
+ updateCurrentText(text);
#if QT_CONFIG(accessibility)
QAccessibleValueChangeEvent event(q, text);
QAccessible::updateAccessibility(&event);
@@ -1402,11 +1451,13 @@ QComboBox::~QComboBox()
Q_D(QComboBox);
QT_TRY {
- disconnect(d->model, SIGNAL(destroyed()),
- this, SLOT(_q_modelDestroyed()));
+ d->disconnectModel();
} QT_CATCH(...) {
; // objects can't throw in destructor
}
+
+ // Dispose of container before QComboBox goes away
+ delete d->container;
}
/*!
@@ -1437,7 +1488,7 @@ void QComboBox::setMaxVisibleItems(int maxItems)
/*!
\property QComboBox::count
- \brief the number of items in the combobox
+ \brief the number of items in the combobox.
By default, for an empty combo box, this property has a value of 0.
*/
@@ -1449,7 +1500,7 @@ int QComboBox::count() const
/*!
\property QComboBox::maxCount
- \brief the maximum number of items allowed in the combobox
+ \brief the maximum number of items allowed in the combobox.
\note If you set the maximum number to be less then the current
amount of items in the combobox, the extra items will be
@@ -1482,7 +1533,7 @@ int QComboBox::maxCount() const
/*!
\property QComboBox::duplicatesEnabled
- \brief whether the user can enter duplicate items into the combobox
+ \brief whether the user can enter duplicate items into the combobox.
Note that it is always possible to programmatically insert duplicate items into the
combobox.
@@ -1528,7 +1579,7 @@ int QComboBox::findData(const QVariant &data, int role, Qt::MatchFlags flags) co
/*!
\property QComboBox::insertPolicy
\brief the policy used to determine where user-inserted items should
- appear in the combobox
+ appear in the combobox.
The default value is \l InsertAtBottom, indicating that new items will appear
at the bottom of the list of items.
@@ -1551,7 +1602,7 @@ void QComboBox::setInsertPolicy(InsertPolicy policy)
/*!
\property QComboBox::sizeAdjustPolicy
\brief the policy describing how the size of the combobox changes
- when the content changes
+ when the content changes.
The default value is \l AdjustToContentsOnFirstShow.
@@ -1642,7 +1693,7 @@ void QComboBox::setIconSize(const QSize &size)
/*!
\property QComboBox::placeholderText
- \brief Sets a \a placeholderText text shown when no valid index is set
+ \brief Sets a \a placeholderText text shown when no valid index is set.
The \a placeholderText will be shown when an invalid index is set. The
text is not accessible in the dropdown list. When this function is called
@@ -1681,7 +1732,7 @@ QString QComboBox::placeholderText() const
/*!
\property QComboBox::editable
- \brief whether the combo box can be edited by the user
+ \brief whether the combo box can be edited by the user.
By default, this property is \c false. The effect of editing depends
on the insert policy.
@@ -1720,6 +1771,8 @@ void QComboBoxPrivate::updateDelegate(bool force)
QIcon QComboBoxPrivate::itemIcon(const QModelIndex &index) const
{
+ if (!index.isValid())
+ return {};
QVariant decoration = model->data(index, Qt::DecorationRole);
if (decoration.userType() == QMetaType::QPixmap)
return QIcon(qvariant_cast<QPixmap>(decoration));
@@ -1790,13 +1843,18 @@ void QComboBox::setLineEdit(QLineEdit *edit)
#endif
if (d->lineEdit->parent() != this)
d->lineEdit->setParent(this);
- connect(d->lineEdit, SIGNAL(returnPressed()), this, SLOT(_q_returnPressed()));
- connect(d->lineEdit, SIGNAL(editingFinished()), this, SLOT(_q_editingFinished()));
- connect(d->lineEdit, SIGNAL(textChanged(QString)), this, SIGNAL(editTextChanged(QString)));
- connect(d->lineEdit, SIGNAL(textChanged(QString)), this, SIGNAL(currentTextChanged(QString)));
- connect(d->lineEdit, SIGNAL(cursorPositionChanged(int,int)), this, SLOT(updateMicroFocus()));
- connect(d->lineEdit, SIGNAL(selectionChanged()), this, SLOT(updateMicroFocus()));
- connect(d->lineEdit->d_func()->control, SIGNAL(updateMicroFocus()), this, SLOT(updateMicroFocus()));
+ QObjectPrivate::connect(d->lineEdit, &QLineEdit::returnPressed,
+ d, &QComboBoxPrivate::returnPressed);
+ QObjectPrivate::connect(d->lineEdit, &QLineEdit::editingFinished,
+ d, &QComboBoxPrivate::editingFinished);
+ connect(d->lineEdit, &QLineEdit::textChanged, this, &QComboBox::editTextChanged);
+ connect(d->lineEdit, &QLineEdit::textChanged, this, &QComboBox::currentTextChanged);
+ QObjectPrivate::connect(d->lineEdit, &QLineEdit::cursorPositionChanged,
+ d, &QComboBoxPrivate::updateMicroFocus);
+ QObjectPrivate::connect(d->lineEdit, &QLineEdit::selectionChanged,
+ d, &QComboBoxPrivate::updateMicroFocus);
+ QObjectPrivate::connect(d->lineEdit->d_func()->control, &QWidgetLineControl::updateMicroFocus,
+ d, &QComboBoxPrivate::updateMicroFocus);
d->lineEdit->setFrame(false);
d->lineEdit->setContextMenuPolicy(Qt::NoContextMenu);
d->updateFocusPolicy();
@@ -1898,7 +1956,8 @@ void QComboBox::setCompleter(QCompleter *c)
}
d->lineEdit->setCompleter(c);
if (c) {
- connect(c, SIGNAL(activated(QModelIndex)), this, SLOT(_q_completerActivated(QModelIndex)));
+ QObjectPrivate::connect(c, QOverload<const QModelIndex &>::of(&QCompleter::activated),
+ d, &QComboBoxPrivate::completerActivated);
c->setWidget(this);
}
}
@@ -1992,59 +2051,60 @@ void QComboBox::setModel(QAbstractItemModel *model)
if (d->lineEdit && d->lineEdit->completer())
d->lineEdit->completer()->setModel(model);
#endif
- if (d->model) {
- disconnect(d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
- this, SLOT(_q_dataChanged(QModelIndex,QModelIndex)));
- disconnect(d->model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
- this, SLOT(_q_updateIndexBeforeChange()));
- disconnect(d->model, SIGNAL(rowsInserted(QModelIndex,int,int)),
- this, SLOT(_q_rowsInserted(QModelIndex,int,int)));
- disconnect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
- this, SLOT(_q_updateIndexBeforeChange()));
- disconnect(d->model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
- this, SLOT(_q_rowsRemoved(QModelIndex,int,int)));
- disconnect(d->model, SIGNAL(destroyed()),
- this, SLOT(_q_modelDestroyed()));
- disconnect(d->model, SIGNAL(modelAboutToBeReset()),
- this, SLOT(_q_updateIndexBeforeChange()));
- disconnect(d->model, SIGNAL(modelReset()),
- this, SLOT(_q_modelReset()));
- if (d->model->QObject::parent() == this)
- delete d->model;
+ d->disconnectModel();
+ if (d->model && d->model->QObject::parent() == this) {
+ delete d->model;
+ d->model = nullptr;
}
d->model = model;
- connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
- this, SLOT(_q_dataChanged(QModelIndex,QModelIndex)));
- connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
- this, SLOT(_q_updateIndexBeforeChange()));
- connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
- this, SLOT(_q_rowsInserted(QModelIndex,int,int)));
- connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
- this, SLOT(_q_updateIndexBeforeChange()));
- connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
- this, SLOT(_q_rowsRemoved(QModelIndex,int,int)));
- connect(model, SIGNAL(destroyed()),
- this, SLOT(_q_modelDestroyed()));
- connect(model, SIGNAL(modelAboutToBeReset()),
- this, SLOT(_q_updateIndexBeforeChange()));
- connect(model, SIGNAL(modelReset()),
- this, SLOT(_q_modelReset()));
-
if (d->container) {
d->container->itemView()->setModel(model);
- connect(d->container->itemView()->selectionModel(),
- SIGNAL(currentChanged(QModelIndex,QModelIndex)),
- this, SLOT(_q_emitHighlighted(QModelIndex)), Qt::UniqueConnection);
+ QObjectPrivate::connect(d->container->itemView()->selectionModel(),
+ &QItemSelectionModel::currentChanged,
+ d, &QComboBoxPrivate::emitHighlighted, Qt::UniqueConnection);
}
+ d->connectModel();
+
setRootModelIndex(QModelIndex());
d->trySetValidIndex();
d->modelChanged();
}
+void QComboBoxPrivate::connectModel()
+{
+ if (!model)
+ return;
+
+ modelConnections = {
+ QObjectPrivate::connect(model, &QAbstractItemModel::dataChanged,
+ this, &QComboBoxPrivate::dataChanged),
+ QObjectPrivate::connect(model, &QAbstractItemModel::rowsAboutToBeInserted,
+ this, &QComboBoxPrivate::updateIndexBeforeChange),
+ QObjectPrivate::connect(model, &QAbstractItemModel::rowsInserted,
+ this, &QComboBoxPrivate::rowsInserted),
+ QObjectPrivate::connect(model, &QAbstractItemModel::rowsAboutToBeRemoved,
+ this, &QComboBoxPrivate::updateIndexBeforeChange),
+ QObjectPrivate::connect(model, &QAbstractItemModel::rowsRemoved,
+ this, &QComboBoxPrivate::rowsRemoved),
+ QObjectPrivate::connect(model, &QObject::destroyed,
+ this, &QComboBoxPrivate::modelDestroyed),
+ QObjectPrivate::connect(model, &QAbstractItemModel::modelAboutToBeReset,
+ this, &QComboBoxPrivate::updateIndexBeforeChange),
+ QObjectPrivate::connect(model, &QAbstractItemModel::modelReset,
+ this, &QComboBoxPrivate::modelReset)
+ };
+}
+
+void QComboBoxPrivate::disconnectModel()
+{
+ for (auto &connection : modelConnections)
+ QObject::disconnect(connection);
+}
+
/*!
Returns the root model item index for the items in the combobox.
@@ -2090,7 +2150,7 @@ int QComboBox::currentIndex() const
void QComboBox::setCurrentIndex(int index)
{
Q_D(QComboBox);
- QModelIndex mi = d->model->index(index, d->modelColumn, d->root);
+ QModelIndex mi = index >= 0 ? d->model->index(index, d->modelColumn, d->root) : QModelIndex();
d->setCurrentIndex(mi);
}
@@ -2127,13 +2187,24 @@ void QComboBoxPrivate::setCurrentIndex(const QModelIndex &mi)
}
updateLineEditGeometry();
}
- // If the model was reset to an empty, currentIndex will be invalidated
+ // If the model was reset to an empty one, currentIndex will be invalidated
// (because it's a QPersistentModelIndex), but the index change will never
- // be advertised. So we need an explicit check for such condition.
+ // be advertised. So an explicit check for this condition is needed.
+ // The variable used for that check has to be reset when a previously valid
+ // index becomes invalid.
const bool modelResetToEmpty = !normalized.isValid() && indexBeforeChange != -1;
+ if (modelResetToEmpty)
+ indexBeforeChange = -1;
+
if (indexChanged || modelResetToEmpty) {
+ QItemSelectionModel::SelectionFlags selectionMode = QItemSelectionModel::ClearAndSelect;
+ if (q->view()->selectionBehavior() == QAbstractItemView::SelectRows)
+ selectionMode.setFlag(QItemSelectionModel::Rows);
+ if (auto *model = q->view()->selectionModel())
+ model->setCurrentIndex(currentIndex, selectionMode);
+
q->update();
- _q_emitCurrentIndexChanged(currentIndex);
+ emitCurrentIndexChanged(currentIndex);
}
}
@@ -2262,7 +2333,7 @@ void QComboBox::insertItem(int index, const QIcon &icon, const QString &text, co
if (!values.isEmpty()) d->model->setItemData(item, values);
}
d->inserting = false;
- d->_q_rowsInserted(d->root, index, index);
+ d->rowsInserted(d->root, index, index);
++itemCount;
} else {
d->inserting = false;
@@ -2310,7 +2381,7 @@ void QComboBox::insertItems(int index, const QStringList &list)
d->model->setData(item, list.at(i), Qt::EditRole);
}
d->inserting = false;
- d->_q_rowsInserted(d->root, index, index + insertCount - 1);
+ d->rowsInserted(d->root, index, index + insertCount - 1);
} else {
d->inserting = false;
}
@@ -2420,8 +2491,11 @@ void QComboBox::setView(QAbstractItemView *itemView)
return;
}
- if (itemView->model() != d->model)
+ if (itemView->model() != d->model) {
+ d->disconnectModel();
itemView->setModel(d->model);
+ d->connectModel();
+ }
d->viewContainer()->setItemView(itemView);
}
@@ -2453,12 +2527,13 @@ void QComboBoxPrivate::cleanupNativePopup()
if (!m_platformMenu)
return;
+ m_platformMenu->setVisible(false);
int count = int(m_platformMenu->tag());
for (int i = 0; i < count; ++i)
m_platformMenu->menuItemAt(i)->deleteLater();
delete m_platformMenu;
- m_platformMenu = 0;
+ m_platformMenu = nullptr;
}
/*!
@@ -2490,15 +2565,17 @@ bool QComboBoxPrivate::showNativePopup()
QVariant textVariant = model->data(rowIndex, Qt::EditRole);
item->setText(textVariant.toString());
QVariant iconVariant = model->data(rowIndex, Qt::DecorationRole);
+ const Qt::ItemFlags itemFlags = model->flags(rowIndex);
if (iconVariant.canConvert<QIcon>())
item->setIcon(iconVariant.value<QIcon>());
item->setCheckable(true);
item->setChecked(i == currentIndex);
+ item->setEnabled(itemFlags & Qt::ItemIsEnabled);
if (!currentItem || i == currentIndex)
currentItem = item;
IndexSetter setter = { i, q };
- QObject::connect(item, &QPlatformMenuItem::activated, setter);
+ QObject::connect(item, &QPlatformMenuItem::activated, q, setter);
m_platformMenu->insertMenuItem(item, 0);
m_platformMenu->syncMenuItem(item);
@@ -2513,15 +2590,18 @@ bool QComboBoxPrivate::showNativePopup()
else if (q->testAttribute(Qt::WA_MacMiniSize))
offset = QPoint(-2, 6);
+ [[maybe_unused]] QPointer<QComboBox> guard(q);
const QRect targetRect = QRect(tlw->mapFromGlobal(q->mapToGlobal(offset)), QSize());
m_platformMenu->showPopup(tlw, QHighDpi::toNativePixels(targetRect, tlw), currentItem);
#ifdef Q_OS_MACOS
- // The Cocoa popup will swallow any mouse release event.
- // We need to fake one here to un-press the button.
- QMouseEvent mouseReleased(QEvent::MouseButtonRelease, q->pos(), q->mapToGlobal(QPoint(0, 0)),
- Qt::LeftButton, Qt::MouseButtons(Qt::LeftButton), {});
- QCoreApplication::sendEvent(q, &mouseReleased);
+ if (guard) {
+ // The Cocoa popup will swallow any mouse release event.
+ // We need to fake one here to un-press the button.
+ QMouseEvent mouseReleased(QEvent::MouseButtonRelease, q->pos(), q->mapToGlobal(QPoint(0, 0)),
+ Qt::LeftButton, Qt::MouseButtons(Qt::LeftButton), {});
+ QCoreApplication::sendEvent(q, &mouseReleased);
+ }
#endif
return true;
@@ -2559,11 +2639,6 @@ void QComboBox::showPopup()
return;
#endif // Q_OS_MAC
- // set current item and select it
- QItemSelectionModel::SelectionFlags selectionMode = QItemSelectionModel::ClearAndSelect;
- if (view()->selectionBehavior() == QAbstractItemView::SelectRows)
- selectionMode.setFlag(QItemSelectionModel::Rows);
- view()->selectionModel()->setCurrentIndex(d->currentIndex, selectionMode);
QComboBoxPrivateContainer* container = d->viewContainer();
QRect listRect(style->subControlRect(QStyle::CC_ComboBox, &opt,
QStyle::SC_ComboBoxListBoxPopup, this));
@@ -2574,6 +2649,7 @@ void QComboBox::showPopup()
QPoint above = mapToGlobal(listRect.topLeft());
int aboveHeight = above.y() - screen.y();
bool boundToScreen = !window()->testAttribute(Qt::WA_DontShowOnScreen);
+ const auto listView = qobject_cast<QListView *>(d->viewContainer()->itemView());
{
int listHeight = 0;
@@ -2588,6 +2664,8 @@ void QComboBox::showPopup()
while (!toCheck.isEmpty()) {
QModelIndex parent = toCheck.pop();
for (int i = 0, end = d->model->rowCount(parent); i < end; ++i) {
+ if (listView && listView->isRowHidden(i))
+ continue;
QModelIndex idx = d->model->index(i, d->modelColumn, parent);
if (!idx.isValid())
continue;
@@ -2661,7 +2739,13 @@ void QComboBox::showPopup()
listRect.moveLeft(above.x());
// Position vertically so the currently selected item lines up
- // with the combo box.
+ // with the combo box. In order to do that, make sure that the item
+ // view is scrolled to the top first, otherwise calls to view()->visualRect()
+ // will return the geometry the selected item had the last time the popup
+ // was visible (and perhaps scrolled). And this will not match the geometry
+ // it will actually have when we resize the container to fit all the items
+ // further down in this function.
+ view()->scrollToTop();
const QRect currentItemRect = view()->visualRect(view()->currentIndex());
const int offset = listRect.top() - currentItemRect.top();
listRect.moveTop(above.y() + offset - listRect.top());
@@ -2794,31 +2878,30 @@ void QComboBox::hidePopup()
return;
#if QT_CONFIG(effects)
- // Flash selected/triggered item (if any).
- if (style()->styleHint(QStyle::SH_Menu_FlashTriggeredItem)) {
- QItemSelectionModel *selectionModel = d->container->itemView()
- ? d->container->itemView()->selectionModel() : nullptr;
- if (selectionModel && selectionModel->hasSelection()) {
- const QItemSelection selection = selectionModel->selection();
-
- QTimer::singleShot(0, d->container, [d, selection, selectionModel]{
+ QItemSelectionModel *selectionModel = d->container->itemView()
+ ? d->container->itemView()->selectionModel() : nullptr;
+ // Flash selected/triggered item (if any) before hiding the popup.
+ if (style()->styleHint(QStyle::SH_Menu_FlashTriggeredItem) &&
+ selectionModel && selectionModel->hasSelection()) {
+ const QItemSelection selection = selectionModel->selection();
+
+ QTimer::singleShot(0, d->container, [d, selection, selectionModel]{
+ QSignalBlocker modelBlocker(d->model);
+ QSignalBlocker viewBlocker(d->container->itemView());
+ QSignalBlocker containerBlocker(d->container);
+
+ // Deselect item and wait 60 ms.
+ selectionModel->select(selection, QItemSelectionModel::Toggle);
+ QTimer::singleShot(60, d->container, [d, selection, selectionModel]{
QSignalBlocker modelBlocker(d->model);
QSignalBlocker viewBlocker(d->container->itemView());
QSignalBlocker containerBlocker(d->container);
-
- // Deselect item and wait 60 ms.
selectionModel->select(selection, QItemSelectionModel::Toggle);
- QTimer::singleShot(60, d->container, [d, selection, selectionModel]{
- QSignalBlocker modelBlocker(d->model);
- QSignalBlocker viewBlocker(d->container->itemView());
- QSignalBlocker containerBlocker(d->container);
- selectionModel->select(selection, QItemSelectionModel::Toggle);
- QTimer::singleShot(20, d->container, [d] {
- d->doHidePopup();
- });
+ QTimer::singleShot(20, d->container, [d] {
+ d->doHidePopup();
});
});
- }
+ });
} else
#endif // QT_CONFIG(effects)
{
@@ -2831,7 +2914,16 @@ void QComboBoxPrivate::doHidePopup()
if (container && container->isVisible())
container->hide();
- _q_resetButton();
+ resetButton();
+}
+
+void QComboBoxPrivate::updateCurrentText(const QString &text)
+{
+ if (text == currentText)
+ return;
+
+ currentText = text;
+ emit q_func()->currentTextChanged(text);
}
/*!
@@ -2914,6 +3006,7 @@ void QComboBox::changeEvent(QEvent *e)
if (d->container)
d->container->updateStyleSettings();
d->updateDelegate();
+
#ifdef Q_OS_MAC
case QEvent::MacSizeChange:
#endif
@@ -3084,7 +3177,10 @@ void QComboBoxPrivate::showPopupFromMouseEvent(QMouseEvent *e)
// viewContainer(), we avoid creating the QComboBoxPrivateContainer.
viewContainer()->initialClickPosition = q->mapToGlobal(e->position().toPoint());
}
+ QPointer<QComboBox> guard = q;
q->showPopup();
+ if (!guard)
+ return;
// The code below ensures that regular mousepress and pick item still works
// If it was not called the viewContainer would ignore event since it didn't have
// a mousePressEvent first.
@@ -3224,8 +3320,9 @@ void QComboBox::keyPressEvent(QKeyEvent *e)
#endif
if (!d->lineEdit) {
- if (!e->text().isEmpty())
- d->keyboardSearchString(e->text());
+ const auto text = e->text();
+ if (!text.isEmpty() && text.at(0).isPrint())
+ d->keyboardSearchString(text);
else
e->ignore();
}
@@ -3423,7 +3520,7 @@ QVariant QComboBox::inputMethodQuery(Qt::InputMethodQuery query, const QVariant
/*!
\property QComboBox::frame
- \brief whether the combo box draws itself with a frame
+ \brief whether the combo box draws itself with a frame.
If enabled (the default) the combo box draws itself inside a
diff --git a/src/widgets/widgets/qcombobox.h b/src/widgets/widgets/qcombobox.h
index be28ce7173..1689d0fa1e 100644
--- a/src/widgets/widgets/qcombobox.h
+++ b/src/widgets/widgets/qcombobox.h
@@ -204,21 +204,6 @@ protected:
private:
Q_DECLARE_PRIVATE(QComboBox)
Q_DISABLE_COPY(QComboBox)
- Q_PRIVATE_SLOT(d_func(), void _q_itemSelected(const QModelIndex &item))
- Q_PRIVATE_SLOT(d_func(), void _q_emitHighlighted(const QModelIndex &))
- Q_PRIVATE_SLOT(d_func(), void _q_emitCurrentIndexChanged(const QModelIndex &index))
- Q_PRIVATE_SLOT(d_func(), void _q_editingFinished())
- Q_PRIVATE_SLOT(d_func(), void _q_returnPressed())
- Q_PRIVATE_SLOT(d_func(), void _q_resetButton())
- Q_PRIVATE_SLOT(d_func(), void _q_dataChanged(const QModelIndex &, const QModelIndex &))
- Q_PRIVATE_SLOT(d_func(), void _q_updateIndexBeforeChange())
- Q_PRIVATE_SLOT(d_func(), void _q_rowsInserted(const QModelIndex & parent, int start, int end))
- Q_PRIVATE_SLOT(d_func(), void _q_rowsRemoved(const QModelIndex & parent, int start, int end))
- Q_PRIVATE_SLOT(d_func(), void _q_modelDestroyed())
- Q_PRIVATE_SLOT(d_func(), void _q_modelReset())
-#if QT_CONFIG(completer)
- Q_PRIVATE_SLOT(d_func(), void _q_completerActivated(const QModelIndex &index))
-#endif
};
inline void QComboBox::addItem(const QString &atext, const QVariant &auserData)
diff --git a/src/widgets/widgets/qcombobox_p.h b/src/widgets/widgets/qcombobox_p.h
index 8e686ac79a..723d637ae2 100644
--- a/src/widgets/widgets/qcombobox_p.h
+++ b/src/widgets/widgets/qcombobox_p.h
@@ -27,7 +27,6 @@
#include "QtGui/qpainter.h"
#include "QtWidgets/qstyle.h"
#include "QtWidgets/qstyleoption.h"
-#include "QtCore/qpair.h"
#include "QtCore/qtimer.h"
#include "private/qwidget_p.h"
#include "QtCore/qpointer.h"
@@ -49,7 +48,11 @@ class QComboBoxListView : public QListView
{
Q_OBJECT
public:
- QComboBoxListView(QComboBox *cmb = nullptr) : combo(cmb) {}
+ QComboBoxListView(QComboBox *cmb = nullptr) : combo(cmb)
+ {
+ if (cmb)
+ setScreen(cmb->screen());
+ }
protected:
void resizeEvent(QResizeEvent *event) override
@@ -180,6 +183,7 @@ class Q_WIDGETS_EXPORT QComboBoxPrivateContainer : public QFrame
public:
QComboBoxPrivateContainer(QAbstractItemView *itemView, QComboBox *parent);
+ ~QComboBoxPrivateContainer();
QAbstractItemView *itemView() const;
void setItemView(QAbstractItemView *itemView);
int spacing() const;
@@ -315,24 +319,25 @@ public:
QComboBoxPrivateContainer* viewContainer();
void updateLineEditGeometry();
Qt::MatchFlags matchFlags() const;
- void _q_editingFinished();
- void _q_returnPressed();
- void _q_complete();
- void _q_itemSelected(const QModelIndex &item);
+ void editingFinished();
+ void returnPressed();
+ void complete();
+ void itemSelected(const QModelIndex &item);
bool contains(const QString &text, int role);
void emitActivated(const QModelIndex &index);
- void _q_emitHighlighted(const QModelIndex &index);
- void _q_emitCurrentIndexChanged(const QModelIndex &index);
- void _q_modelDestroyed();
- void _q_modelReset();
+ void emitHighlighted(const QModelIndex &index);
+ void emitCurrentIndexChanged(const QModelIndex &index);
+ void modelDestroyed();
+ void modelReset();
+ void updateMicroFocus() { q_func()->updateMicroFocus(); } // PMF connect doesn't handle default args
#if QT_CONFIG(completer)
- void _q_completerActivated(const QModelIndex &index);
+ void completerActivated(const QModelIndex &index);
#endif
- void _q_resetButton();
- void _q_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
- void _q_updateIndexBeforeChange();
- void _q_rowsInserted(const QModelIndex &parent, int start, int end);
- void _q_rowsRemoved(const QModelIndex &parent, int start, int end);
+ void resetButton();
+ void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
+ void updateIndexBeforeChange();
+ void rowsInserted(const QModelIndex &parent, int start, int end);
+ void rowsRemoved(const QModelIndex &parent, int start, int end);
void updateArrow(QStyle::StateFlag state);
bool updateHoverControl(const QPoint &pos);
void trySetValidIndex();
@@ -353,6 +358,9 @@ public:
void updateFocusPolicy();
void showPopupFromMouseEvent(QMouseEvent *e);
void doHidePopup();
+ void updateCurrentText(const QString &text);
+ void connectModel();
+ void disconnectModel();
#ifdef Q_OS_MAC
void cleanupNativePopup();
@@ -369,15 +377,17 @@ public:
};
#endif
+ std::array<QMetaObject::Connection, 8> modelConnections;
QAbstractItemModel *model = nullptr;
QLineEdit *lineEdit = nullptr;
- QComboBoxPrivateContainer *container = nullptr;
+ QPointer<QComboBoxPrivateContainer> container;
#ifdef Q_OS_MAC
QPlatformMenu *m_platformMenu = nullptr;
#endif
QPersistentModelIndex currentIndex;
QPersistentModelIndex root;
QString placeholderText;
+ QString currentText;
QRect hoverRect;
QSize iconSize;
mutable QSize minimumSizeHint;
diff --git a/src/widgets/widgets/qcommandlinkbutton.cpp b/src/widgets/widgets/qcommandlinkbutton.cpp
index 2c59e78c1a..5c7d2601d9 100644
--- a/src/widgets/widgets/qcommandlinkbutton.cpp
+++ b/src/widgets/widgets/qcommandlinkbutton.cpp
@@ -172,7 +172,7 @@ bool QCommandLinkButtonPrivate::usingVistaStyle() const
//### This is a hack to detect if we are indeed running Vista style themed and not in classic
// When we add api to query for this, we should change this implementation to use it.
return q->property("_qt_usingVistaStyle").toBool()
- && q->style()->pixelMetric(QStyle::PM_ButtonShiftHorizontal, nullptr) == 0;
+ && q->style()->pixelMetric(QStyle::PM_ButtonShiftHorizontal, nullptr, q) == 0;
}
void QCommandLinkButtonPrivate::init()
@@ -189,7 +189,7 @@ void QCommandLinkButtonPrivate::init()
q->setIconSize(QSize(20, 20));
QStyleOptionButton opt;
q->initStyleOption(&opt);
- q->setIcon(q->style()->standardIcon(QStyle::SP_CommandLink, &opt));
+ q->setIcon(q->style()->standardIcon(QStyle::SP_CommandLink, &opt, q));
}
// Calculates the height of the description text based on widget width
@@ -325,9 +325,9 @@ void QCommandLinkButton::paintEvent(QPaintEvent *)
QSize pixmapSize = icon().actualSize(iconSize());
const int vOffset = isDown()
- ? style()->pixelMetric(QStyle::PM_ButtonShiftVertical, &option) : 0;
+ ? style()->pixelMetric(QStyle::PM_ButtonShiftVertical, &option, this) : 0;
const int hOffset = isDown()
- ? style()->pixelMetric(QStyle::PM_ButtonShiftHorizontal, &option) : 0;
+ ? style()->pixelMetric(QStyle::PM_ButtonShiftHorizontal, &option, this) : 0;
//Draw icon
p.drawControl(QStyle::CE_PushButton, option);
diff --git a/src/widgets/widgets/qdatetimeedit.cpp b/src/widgets/widgets/qdatetimeedit.cpp
index 61cf20891b..01e52b2fa6 100644
--- a/src/widgets/widgets/qdatetimeedit.cpp
+++ b/src/widgets/widgets/qdatetimeedit.cpp
@@ -219,7 +219,7 @@ QDateTimeEdit::~QDateTimeEdit()
widget's date-range to start and end on the date of the new value of this
property.
- \sa date, time, minimumDateTime, maximumDateTime
+ \sa date, time, minimumDateTime, maximumDateTime, timeZone
*/
QDateTime QDateTimeEdit::dateTime() const
@@ -948,7 +948,7 @@ void QDateTimeEdit::setDisplayFormat(const QString &format)
d->value = d->value.toDate().startOfDay(d->timeZone);
}
d->updateEdit();
- d->_q_editorCursorPositionChanged(-1, 0);
+ d->editorCursorPositionChanged(-1, 0);
}
}
@@ -984,10 +984,16 @@ void QDateTimeEdit::setCalendarPopup(bool enable)
update();
}
+#if QT_DEPRECATED_SINCE(6, 10)
/*!
\property QDateTimeEdit::timeSpec
- \brief The current timespec used by the date time edit.
\since 4.4
+ \deprecated[6.10] Use QDateTimeEdit::timeZone instead.
+ \brief The current timespec used by the date time edit.
+
+ Since Qt 6.7 this is an indirect accessor for the timeZone property.
+
+ \sa QDateTimeEdit::timeZone
*/
Qt::TimeSpec QDateTimeEdit::timeSpec() const
@@ -1002,17 +1008,45 @@ void QDateTimeEdit::setTimeSpec(Qt::TimeSpec spec)
if (spec != d->timeZone.timeSpec()) {
switch (spec) {
case Qt::UTC:
- d->timeZone = QTimeZone::UTC;
+ setTimeZone(QTimeZone::UTC);
break;
case Qt::LocalTime:
- d->timeZone = QTimeZone::LocalTime;
+ setTimeZone(QTimeZone::LocalTime);
break;
default:
qWarning() << "Ignoring attempt to set time-spec" << spec
- << "which is not yet supported by QDateTimeEdit";
- // TODO: fix that QTBUG-80417.
+ << "which needs ancillary data: see setTimeZone()";
return;
}
+ }
+}
+#endif // 6.10 deprecation
+
+// TODO: enable user input to control timeZone, when the format includes it.
+/*!
+ \property QDateTimeEdit::timeZone
+ \since 6.7
+ \brief The current timezone used by the datetime editing widget
+
+ If the datetime format in use includes a timezone indicator - that is, a
+ \c{t}, \c{tt}, \c{ttt} or \c{tttt} format specifier - the user's input is
+ re-expressed in this timezone whenever it is parsed, overriding any timezone
+ the user may have specified.
+
+ \sa QDateTimeEdit::displayFormat
+*/
+
+QTimeZone QDateTimeEdit::timeZone() const
+{
+ Q_D(const QDateTimeEdit);
+ return d->timeZone;
+}
+
+void QDateTimeEdit::setTimeZone(const QTimeZone &zone)
+{
+ Q_D(QDateTimeEdit);
+ if (zone != d->timeZone) {
+ d->timeZone = zone;
d->updateTimeZone();
}
}
@@ -1297,6 +1331,7 @@ void QDateTimeEdit::focusInEvent(QFocusEvent *event)
case Qt::ActiveWindowFocusReason:
if (oldHasHadFocus)
return;
+ break;
case Qt::ShortcutFocusReason:
case Qt::TabFocusReason:
default:
@@ -1421,8 +1456,6 @@ QDateTime QDateTimeEdit::dateTimeFromText(const QString &text) const
QString copy = text;
int pos = d->edit->cursorPosition();
QValidator::State state = QValidator::Acceptable;
- // TODO: if the format specifies time-zone, d->timeZone should change as
- // determined by the parsed text.
return d->validateAndInterpret(copy, pos, state);
}
@@ -1450,16 +1483,11 @@ void QDateTimeEdit::fixup(QString &input) const
int copy = d->edit->cursorPosition();
QDateTime value = d->validateAndInterpret(input, copy, state, true);
- /*
- String was valid, but the datetime still is not; use the time that
- has the same distance from epoch.
- CorrectToPreviousValue correction is handled by QAbstractSpinBox.
- */
- if (!value.isValid() && d->correctionMode == QAbstractSpinBox::CorrectToNearestValue) {
- value = QDateTime::fromMSecsSinceEpoch(value.toMSecsSinceEpoch(),
- value.timeRepresentation());
+ // CorrectToPreviousValue correction is handled by QAbstractSpinBox.
+ // The value might not match the input if the input represents a date-time
+ // skipped over by its time representation, such as a spring-forward.
+ if (d->correctionMode == QAbstractSpinBox::CorrectToNearestValue)
input = textFromDateTime(value);
- }
}
@@ -1727,11 +1755,7 @@ QDateTime QDateTimeEditPrivate::convertTimeZone(const QDateTime &datetime)
QDateTime QDateTimeEditPrivate::dateTimeValue(QDate date, QTime time) const
{
- QDateTime when = QDateTime(date, time, timeZone);
- if (when.isValid())
- return when;
- // Hit a spring-forward gap
- return QDateTime::fromMSecsSinceEpoch(when.toMSecsSinceEpoch(), timeZone);
+ return QDateTime(date, time, timeZone);
}
void QDateTimeEditPrivate::updateTimeZone()
@@ -2013,6 +2037,8 @@ QDateTime QDateTimeEditPrivate::validateAndInterpret(QString &input, int &positi
StateNode tmp = parse(input, position, value.toDateTime(), fixup);
// Take note of any corrections imposed during parsing:
input = m_text;
+ // TODO: if the format specifies time-zone, update timeZone to match the
+ // parsed text; but we're in const context, so can't - QTBUG-118393.
// Impose this widget's time system:
tmp.value = tmp.value.toTimeZone(timeZone);
// ... but that might turn a valid datetime into an invalid one:
@@ -2135,11 +2161,10 @@ QDateTime QDateTimeEditPrivate::stepBy(int sectionIndex, int steps, bool test) c
true when date and time are valid, even if the date-time returned
isn't), so use the time that has the same distance from epoch.
*/
- if (setDigit(v, sectionIndex, val) && !v.isValid()) {
- auto msecsSinceEpoch = v.toMSecsSinceEpoch();
+ if (setDigit(v, sectionIndex, val) && getDigit(v, sectionIndex) != val
+ && sn.type & HourSectionMask && steps < 0) {
// decreasing from e.g 3am to 2am would get us back to 3am, but we want 1am
- if (steps < 0 && sn.type & HourSectionMask)
- msecsSinceEpoch -= 3600 * 1000;
+ auto msecsSinceEpoch = v.toMSecsSinceEpoch() - 3600 * 1000;
v = QDateTime::fromMSecsSinceEpoch(msecsSinceEpoch, v.timeRepresentation());
}
// if this sets year or month it will make
@@ -2272,7 +2297,7 @@ void QDateTimeEditPrivate::emitSignals(EmitPolicy ep, const QVariant &old)
\internal
*/
-void QDateTimeEditPrivate::_q_editorCursorPositionChanged(int oldpos, int newpos)
+void QDateTimeEditPrivate::editorCursorPositionChanged(int oldpos, int newpos)
{
if (ignoreCursorPositionChanged || specialValue())
return;
@@ -2330,7 +2355,7 @@ void QDateTimeEditPrivate::_q_editorCursorPositionChanged(int oldpos, int newpos
currentSectionIndex = s;
Q_ASSERT_X(currentSectionIndex < sectionNodes.size(),
- "QDateTimeEditPrivate::_q_editorCursorPositionChanged()",
+ "QDateTimeEditPrivate::editorCursorPositionChanged()",
qPrintable(QString::fromLatin1("Internal error (%1 %2)").
arg(currentSectionIndex).
arg(sectionNodes.size())));
diff --git a/src/widgets/widgets/qdatetimeedit.h b/src/widgets/widgets/qdatetimeedit.h
index acef0c5359..f7c0dd7374 100644
--- a/src/widgets/widgets/qdatetimeedit.h
+++ b/src/widgets/widgets/qdatetimeedit.h
@@ -5,7 +5,7 @@
#define QDATETIMEEDIT_H
#include <QtWidgets/qtwidgetsglobal.h>
-#include <QtCore/qdatetime.h>
+#include <QtCore/qtimezone.h>
#include <QtCore/qcalendar.h>
#include <QtCore/qvariant.h>
#include <QtWidgets/qabstractspinbox.h>
@@ -39,7 +39,10 @@ class Q_WIDGETS_EXPORT QDateTimeEdit : public QAbstractSpinBox
Q_PROPERTY(bool calendarPopup READ calendarPopup WRITE setCalendarPopup)
Q_PROPERTY(int currentSectionIndex READ currentSectionIndex WRITE setCurrentSectionIndex)
Q_PROPERTY(int sectionCount READ sectionCount)
+#if QT_DEPRECATED_SINCE(6, 10)
Q_PROPERTY(Qt::TimeSpec timeSpec READ timeSpec WRITE setTimeSpec)
+#endif
+ Q_PROPERTY(QTimeZone timeZone READ timeZone WRITE setTimeZone)
public:
enum Section { // a sub-type of QDateTimeParser's like-named enum.
NoSection = 0x0000,
@@ -125,8 +128,14 @@ public:
bool calendarPopup() const;
void setCalendarPopup(bool enable);
+#if QT_DEPRECATED_SINCE(6, 10)
+ QT_DEPRECATED_VERSION_X_6_10("Use timeZone() instead")
Qt::TimeSpec timeSpec() const;
+ QT_DEPRECATED_VERSION_X_6_10("Use setTimeZone() instead")
void setTimeSpec(Qt::TimeSpec spec);
+#endif
+ QTimeZone timeZone() const;
+ void setTimeZone(const QTimeZone &zone);
QSize sizeHint() const override;
diff --git a/src/widgets/widgets/qdatetimeedit_p.h b/src/widgets/widgets/qdatetimeedit_p.h
index cfabf65dda..215ee75bfe 100644
--- a/src/widgets/widgets/qdatetimeedit_p.h
+++ b/src/widgets/widgets/qdatetimeedit_p.h
@@ -28,6 +28,7 @@
#include "private/qdatetimeparser_p.h"
#include "qdebug.h"
+#include <QtCore/qpointer.h>
QT_BEGIN_NAMESPACE
@@ -49,7 +50,7 @@ public:
void emitSignals(EmitPolicy ep, const QVariant &old) override;
QString textFromValue(const QVariant &f) const override;
QVariant valueFromText(const QString &f) const override;
- void _q_editorCursorPositionChanged(int oldpos, int newpos) override;
+ void editorCursorPositionChanged(int oldpos, int newpos) override;
void interpret(EmitPolicy ep) override;
void clearCache() const override;
QStyle::SubControl newHoverControl(const QPoint &pos) override;
diff --git a/src/widgets/widgets/qdial.cpp b/src/widgets/widgets/qdial.cpp
index e16b6eadaf..14e96f8dc3 100644
--- a/src/widgets/widgets/qdial.cpp
+++ b/src/widgets/widgets/qdial.cpp
@@ -58,6 +58,8 @@ int QDialPrivate::bound(int val) const
if (wrapping) {
if ((val >= minimum) && (val <= maximum))
return val;
+ if (minimum == maximum)
+ return minimum;
val = minimum + ((val - minimum) % (maximum - minimum));
if (val < minimum)
val += maximum - minimum;
diff --git a/src/widgets/widgets/qdialogbuttonbox.cpp b/src/widgets/widgets/qdialogbuttonbox.cpp
index 1f8bdc0f94..30c68ad18b 100644
--- a/src/widgets/widgets/qdialogbuttonbox.cpp
+++ b/src/widgets/widgets/qdialogbuttonbox.cpp
@@ -14,6 +14,9 @@
#include <QtGui/qaction.h>
#include "qdialogbuttonbox.h"
+#include "qdialogbuttonbox_p.h"
+
+#include <QtCore/qpointer.h>
QT_BEGIN_NAMESPACE
@@ -53,7 +56,7 @@ QT_BEGIN_NAMESPACE
the buttons (or button texts) yourself and add them to the button box,
specifying their role.
- \snippet dialogs/extension/finddialog.cpp 1
+ \snippet dialogs/dialogs.cpp buttonbox
Alternatively, QDialogButtonBox provides several standard buttons (e.g. OK, Cancel, Save)
that you can use. They exist as flags so you can OR them together in the constructor.
@@ -111,39 +114,25 @@ QT_BEGIN_NAMESPACE
\sa QMessageBox, QPushButton, QDialog
*/
-
-class QDialogButtonBoxPrivate : public QWidgetPrivate
+QDialogButtonBoxPrivate::QDialogButtonBoxPrivate(Qt::Orientation orient)
+ : orientation(orient), buttonLayout(nullptr), center(false)
{
- Q_DECLARE_PUBLIC(QDialogButtonBox)
-
-public:
- QDialogButtonBoxPrivate(Qt::Orientation orient);
-
- QList<QAbstractButton *> buttonLists[QDialogButtonBox::NRoles];
- QHash<QPushButton *, QDialogButtonBox::StandardButton> standardButtonHash;
-
- Qt::Orientation orientation;
- QDialogButtonBox::ButtonLayout layoutPolicy;
- QBoxLayout *buttonLayout;
- bool internalRemove;
- bool center;
+ struct EventFilter : public QObject
+ {
+ EventFilter(QDialogButtonBoxPrivate *d) : d(d) {};
+
+ bool eventFilter(QObject *obj, QEvent *event) override
+ {
+ QAbstractButton *button = qobject_cast<QAbstractButton *>(obj);
+ return button ? d->handleButtonShowAndHide(button, event) : false;
+ }
- void createStandardButtons(QDialogButtonBox::StandardButtons buttons);
+ private:
+ QDialogButtonBoxPrivate *d;
- void layoutButtons();
- void initLayout();
- void resetLayout();
- QPushButton *createButton(QDialogButtonBox::StandardButton button, bool doLayout = true);
- void addButton(QAbstractButton *button, QDialogButtonBox::ButtonRole role, bool doLayout = true);
- void _q_handleButtonDestroyed();
- void _q_handleButtonClicked();
- void addButtonsToLayout(const QList<QAbstractButton *> &buttonList, bool reverse);
- void retranslateStrings();
-};
+ };
-QDialogButtonBoxPrivate::QDialogButtonBoxPrivate(Qt::Orientation orient)
- : orientation(orient), buttonLayout(nullptr), internalRemove(false), center(false)
-{
+ filter.reset(new EventFilter(this));
}
void QDialogButtonBoxPrivate::initLayout()
@@ -177,7 +166,6 @@ void QDialogButtonBoxPrivate::initLayout()
void QDialogButtonBoxPrivate::resetLayout()
{
- //delete buttonLayout;
initLayout();
layoutButtons();
}
@@ -201,6 +189,7 @@ void QDialogButtonBoxPrivate::layoutButtons()
Q_Q(QDialogButtonBox);
const int MacGap = 36 - 8; // 8 is the default gap between a widget and a spacer item
+ QBoolBlocker blocker(ignoreShowAndHide);
for (int i = buttonLayout->count() - 1; i >= 0; --i) {
QLayoutItem *item = buttonLayout->takeAt(i);
if (QWidget *widget = item->widget())
@@ -293,16 +282,20 @@ void QDialogButtonBoxPrivate::layoutButtons()
++currentLayout;
}
- QWidget *lastWidget = nullptr;
- q->setFocusProxy(nullptr);
+ QWidgetList layoutWidgets;
for (int i = 0; i < buttonLayout->count(); ++i) {
- QLayoutItem *item = buttonLayout->itemAt(i);
- if (QWidget *widget = item->widget()) {
- if (lastWidget)
- QWidget::setTabOrder(lastWidget, widget);
- else
- q->setFocusProxy(widget);
- lastWidget = widget;
+ if (auto *widget = buttonLayout->itemAt(i)->widget())
+ layoutWidgets << widget;
+ }
+
+ q->setFocusProxy(nullptr);
+ if (!layoutWidgets.isEmpty()) {
+ QWidget *prev = layoutWidgets.constLast();
+ for (QWidget *here : layoutWidgets) {
+ QWidget::setTabOrder(prev, here);
+ prev = here;
+ if (auto *pushButton = qobject_cast<QPushButton *>(prev); pushButton && pushButton->isDefault())
+ q->setFocusProxy(pushButton);
}
}
@@ -311,7 +304,7 @@ void QDialogButtonBoxPrivate::layoutButtons()
}
QPushButton *QDialogButtonBoxPrivate::createButton(QDialogButtonBox::StandardButton sbutton,
- bool doLayout)
+ LayoutRule layoutRule)
{
Q_Q(QDialogButtonBox);
int icon = 0;
@@ -386,7 +379,7 @@ QPushButton *QDialogButtonBoxPrivate::createButton(QDialogButtonBox::StandardBut
if (Q_UNLIKELY(role == QPlatformDialogHelper::InvalidRole))
qWarning("QDialogButtonBox::createButton: Invalid ButtonRole, button not added");
else
- addButton(button, static_cast<QDialogButtonBox::ButtonRole>(role), doLayout);
+ addButton(button, static_cast<QDialogButtonBox::ButtonRole>(role), layoutRule);
#if QT_CONFIG(shortcut)
const QKeySequence standardShortcut = QGuiApplicationPrivate::platformTheme()->standardButtonShortcut(sbutton);
if (!standardShortcut.isEmpty())
@@ -396,23 +389,36 @@ QPushButton *QDialogButtonBoxPrivate::createButton(QDialogButtonBox::StandardBut
}
void QDialogButtonBoxPrivate::addButton(QAbstractButton *button, QDialogButtonBox::ButtonRole role,
- bool doLayout)
+ LayoutRule layoutRule, AddRule addRule)
{
- Q_Q(QDialogButtonBox);
- QObject::connect(button, SIGNAL(clicked()), q, SLOT(_q_handleButtonClicked()));
- QObject::connect(button, SIGNAL(destroyed()), q, SLOT(_q_handleButtonDestroyed()));
buttonLists[role].append(button);
- if (doLayout)
+ switch (addRule) {
+ case AddRule::Connect:
+ QObjectPrivate::connect(button, &QAbstractButton::clicked,
+ this, &QDialogButtonBoxPrivate::handleButtonClicked);
+ QObjectPrivate::connect(button, &QAbstractButton::destroyed,
+ this, &QDialogButtonBoxPrivate::handleButtonDestroyed);
+ button->installEventFilter(filter.get());
+ break;
+ case AddRule::SkipConnect:
+ break;
+ }
+
+ switch (layoutRule) {
+ case LayoutRule::DoLayout:
layoutButtons();
+ break;
+ case LayoutRule::SkipLayout:
+ break;
+ }
}
void QDialogButtonBoxPrivate::createStandardButtons(QDialogButtonBox::StandardButtons buttons)
{
uint i = QDialogButtonBox::FirstButton;
while (i <= QDialogButtonBox::LastButton) {
- if (i & buttons) {
- createButton(QDialogButtonBox::StandardButton(i), false);
- }
+ if (i & buttons)
+ createButton(QDialogButtonBox::StandardButton(i), LayoutRule::SkipLayout);
i = i << 1;
}
layoutButtons();
@@ -420,13 +426,10 @@ void QDialogButtonBoxPrivate::createStandardButtons(QDialogButtonBox::StandardBu
void QDialogButtonBoxPrivate::retranslateStrings()
{
- typedef QHash<QPushButton *, QDialogButtonBox::StandardButton>::iterator Iterator;
-
- const Iterator end = standardButtonHash.end();
- for (Iterator it = standardButtonHash.begin(); it != end; ++it) {
- const QString text = QGuiApplicationPrivate::platformTheme()->standardButtonText(it.value());
+ for (auto &&[key, value] : std::as_const(standardButtonHash).asKeyValueRange()) {
+ const QString text = QGuiApplicationPrivate::platformTheme()->standardButtonText(value);
if (!text.isEmpty())
- it.key()->setText(text);
+ key->setText(text);
}
}
@@ -482,6 +485,14 @@ QDialogButtonBox::QDialogButtonBox(StandardButtons buttons, Qt::Orientation orie
*/
QDialogButtonBox::~QDialogButtonBox()
{
+ Q_D(QDialogButtonBox);
+
+ d->ignoreShowAndHide = true;
+
+ // QObjectPrivate::connect requires explicit disconnect in destructor
+ // otherwise the connection may kick in on child destruction and reach
+ // the parent's destroyed private object
+ d->disconnectAll();
}
/*!
@@ -636,31 +647,42 @@ void QDialogButtonBox::clear()
d->standardButtonHash.clear();
for (int i = 0; i < NRoles; ++i) {
QList<QAbstractButton *> &list = d->buttonLists[i];
- while (list.size()) {
- QAbstractButton *button = list.takeAt(0);
- QObject::disconnect(button, SIGNAL(destroyed()), this, SLOT(_q_handleButtonDestroyed()));
+ for (auto button : std::as_const(list)) {
+ QObjectPrivate::disconnect(button, &QAbstractButton::destroyed,
+ d, &QDialogButtonBoxPrivate::handleButtonDestroyed);
delete button;
}
+ list.clear();
}
}
/*!
- Returns a list of all the buttons that have been added to the button box.
+ Returns a list of all buttons that have been added to the button box.
\sa buttonRole(), addButton(), removeButton()
*/
QList<QAbstractButton *> QDialogButtonBox::buttons() const
{
Q_D(const QDialogButtonBox);
+ return d->allButtons();
+}
+
+QList<QAbstractButton *> QDialogButtonBoxPrivate::visibleButtons() const
+{
QList<QAbstractButton *> finalList;
- for (int i = 0; i < NRoles; ++i) {
- const QList<QAbstractButton *> &list = d->buttonLists[i];
+ for (int i = 0; i < QDialogButtonBox::NRoles; ++i) {
+ const QList<QAbstractButton *> &list = buttonLists[i];
for (int j = 0; j < list.size(); ++j)
finalList.append(list.at(j));
}
return finalList;
}
+QList<QAbstractButton *> QDialogButtonBoxPrivate::allButtons() const
+{
+ return visibleButtons() << hiddenButtons.keys();
+}
+
/*!
Returns the button role for the specified \a button. This function returns
\l InvalidRole if \a button is \nullptr or has not been added to the button box.
@@ -677,7 +699,7 @@ QDialogButtonBox::ButtonRole QDialogButtonBox::buttonRole(QAbstractButton *butto
return ButtonRole(i);
}
}
- return InvalidRole;
+ return d->hiddenButtons.value(button, InvalidRole);
}
/*!
@@ -688,27 +710,45 @@ QDialogButtonBox::ButtonRole QDialogButtonBox::buttonRole(QAbstractButton *butto
void QDialogButtonBox::removeButton(QAbstractButton *button)
{
Q_D(QDialogButtonBox);
+ d->removeButton(button, QDialogButtonBoxPrivate::RemoveReason::ManualRemove);
+}
+/*!
+ \internal
+ Removes \param button.
+ \param reason determines the behavior following the removal:
+ \list
+ \li \c ManualRemove disconnects all signals and removes the button from standardButtonHash.
+ \li \c HideEvent keeps connections alive, standard buttons remain in standardButtonHash.
+ \li \c Destroyed removes the button from standardButtonHash. Signals remain untouched, because
+ the button might already be only a QObject, the destructor of which handles disconnecting.
+ \endlist
+ */
+void QDialogButtonBoxPrivate::removeButton(QAbstractButton *button, RemoveReason reason)
+{
if (!button)
return;
- // Remove it from the standard button hash first and then from the roles
- d->standardButtonHash.remove(reinterpret_cast<QPushButton *>(button));
- for (int i = 0; i < NRoles; ++i) {
- QList<QAbstractButton *> &list = d->buttonLists[i];
- for (int j = 0; j < list.size(); ++j) {
- if (list.at(j) == button) {
- list.takeAt(j);
- if (!d->internalRemove) {
- disconnect(button, SIGNAL(clicked()), this, SLOT(_q_handleButtonClicked()));
- disconnect(button, SIGNAL(destroyed()), this, SLOT(_q_handleButtonDestroyed()));
- }
- break;
- }
- }
- }
- if (!d->internalRemove)
+ // Remove button from hidden buttons and roles
+ hiddenButtons.remove(button);
+ for (int i = 0; i < QDialogButtonBox::NRoles; ++i)
+ buttonLists[i].removeOne(button);
+
+ switch (reason) {
+ case RemoveReason::ManualRemove:
button->setParent(nullptr);
+ QObjectPrivate::disconnect(button, &QAbstractButton::clicked,
+ this, &QDialogButtonBoxPrivate::handleButtonClicked);
+ QObjectPrivate::disconnect(button, &QAbstractButton::destroyed,
+ this, &QDialogButtonBoxPrivate::handleButtonDestroyed);
+ button->removeEventFilter(filter.get());
+ Q_FALLTHROUGH();
+ case RemoveReason::Destroyed:
+ standardButtonHash.remove(reinterpret_cast<QPushButton *>(button));
+ break;
+ case RemoveReason::HideEvent:
+ break;
+ }
}
/*!
@@ -778,8 +818,8 @@ void QDialogButtonBox::setStandardButtons(StandardButtons buttons)
{
Q_D(QDialogButtonBox);
// Clear out all the old standard buttons, then recreate them.
- qDeleteAll(d->standardButtonHash.keys());
- d->standardButtonHash.clear();
+ const auto toDelete = std::exchange(d->standardButtonHash, {});
+ qDeleteAll(toDelete.keyBegin(), toDelete.keyEnd());
d->createStandardButtons(buttons);
}
@@ -820,7 +860,7 @@ QDialogButtonBox::StandardButton QDialogButtonBox::standardButton(QAbstractButto
return d->standardButtonHash.value(static_cast<QPushButton *>(button));
}
-void QDialogButtonBoxPrivate::_q_handleButtonClicked()
+void QDialogButtonBoxPrivate::handleButtonClicked()
{
Q_Q(QDialogButtonBox);
if (QAbstractButton *button = qobject_cast<QAbstractButton *>(q->sender())) {
@@ -854,13 +894,44 @@ void QDialogButtonBoxPrivate::_q_handleButtonClicked()
}
}
-void QDialogButtonBoxPrivate::_q_handleButtonDestroyed()
+void QDialogButtonBoxPrivate::handleButtonDestroyed()
+{
+ Q_Q(QDialogButtonBox);
+ if (QObject *object = q->sender())
+ removeButton(reinterpret_cast<QAbstractButton *>(object), RemoveReason::Destroyed);
+}
+
+bool QDialogButtonBoxPrivate::handleButtonShowAndHide(QAbstractButton *button, QEvent *event)
{
Q_Q(QDialogButtonBox);
- if (QObject *object = q->sender()) {
- QBoolBlocker skippy(internalRemove);
- q->removeButton(reinterpret_cast<QAbstractButton *>(object));
+
+ const QEvent::Type type = event->type();
+
+ if ((type != QEvent::HideToParent && type != QEvent::ShowToParent) || ignoreShowAndHide)
+ return false;
+
+ switch (type) {
+ case QEvent::HideToParent: {
+ const QDialogButtonBox::ButtonRole role = q->buttonRole(button);
+ if (role != QDialogButtonBox::ButtonRole::InvalidRole) {
+ removeButton(button, RemoveReason::HideEvent);
+ hiddenButtons.insert(button, role);
+ layoutButtons();
+ }
+ break;
+ }
+ case QEvent::ShowToParent:
+ if (hiddenButtons.contains(button)) {
+ const auto role = hiddenButtons.take(button);
+ addButton(button, role, LayoutRule::DoLayout, AddRule::SkipConnect);
+ if (role == QDialogButtonBox::AcceptRole)
+ ensureFirstAcceptIsDefault();
+ }
+ break;
+ default: break;
}
+
+ return false;
}
/*!
@@ -918,36 +989,80 @@ void QDialogButtonBox::changeEvent(QEvent *event)
}
}
+void QDialogButtonBoxPrivate::ensureFirstAcceptIsDefault()
+{
+ Q_Q(QDialogButtonBox);
+ const QList<QAbstractButton *> &acceptRoleList = buttonLists[QDialogButtonBox::AcceptRole];
+ QPushButton *firstAcceptButton = acceptRoleList.isEmpty()
+ ? nullptr
+ : qobject_cast<QPushButton *>(acceptRoleList.at(0));
+
+ if (!firstAcceptButton)
+ return;
+
+ bool hasDefault = false;
+ QWidget *dialog = nullptr;
+ QWidget *p = q;
+ while (p && !p->isWindow()) {
+ p = p->parentWidget();
+ if ((dialog = qobject_cast<QDialog *>(p)))
+ break;
+ }
+
+ QWidget *parent = dialog ? dialog : q;
+ Q_ASSERT(parent);
+
+ const auto pushButtons = parent->findChildren<QPushButton *>();
+ for (QPushButton *pushButton : pushButtons) {
+ if (pushButton->isDefault() && pushButton != firstAcceptButton) {
+ hasDefault = true;
+ break;
+ }
+ }
+ if (!hasDefault && firstAcceptButton) {
+ firstAcceptButton->setDefault(true);
+ // When the QDialogButtonBox is focused, and it doesn't have an
+ // explicit focus widget, it will transfer focus to its focus
+ // proxy, which is the first button in the layout. This behavior,
+ // combined with the behavior that QPushButtons in a QDialog will
+ // by default have their autoDefault set to true, results in the
+ // focus proxy/first button stealing the default button status
+ // immediately when the button box is focused, which is not what
+ // we want. Account for this by explicitly making the firstAcceptButton
+ // focused as well, unless an explicit focus widget has been set, or
+ // a dialog child has Qt::StrongFocus.
+ if (dialog && !(QWidgetPrivate::get(dialog)->hasChildWithFocusPolicy(Qt::StrongFocus, q)
+ || dialog->focusWidget()))
+ firstAcceptButton->setFocus();
+ }
+}
+
+void QDialogButtonBoxPrivate::disconnectAll()
+{
+ Q_Q(QDialogButtonBox);
+ const auto buttons = q->findChildren<QAbstractButton *>();
+ for (auto *button : buttons)
+ button->disconnect(q);
+}
+
/*!
\reimp
*/
bool QDialogButtonBox::event(QEvent *event)
{
Q_D(QDialogButtonBox);
- if (event->type() == QEvent::Show) {
- QList<QAbstractButton *> acceptRoleList = d->buttonLists[AcceptRole];
- QPushButton *firstAcceptButton = acceptRoleList.isEmpty() ? 0 : qobject_cast<QPushButton *>(acceptRoleList.at(0));
- bool hasDefault = false;
- QWidget *dialog = nullptr;
- QWidget *p = this;
- while (p && !p->isWindow()) {
- p = p->parentWidget();
- if ((dialog = qobject_cast<QDialog *>(p)))
- break;
- }
+ switch (event->type()) {
+ case QEvent::Show:
+ d->ensureFirstAcceptIsDefault();
+ break;
- const auto pbs = (dialog ? dialog : this)->findChildren<QPushButton *>();
- for (QPushButton *pb : pbs) {
- if (pb->isDefault() && pb != firstAcceptButton) {
- hasDefault = true;
- break;
- }
- }
- if (!hasDefault && firstAcceptButton)
- firstAcceptButton->setDefault(true);
- }else if (event->type() == QEvent::LanguageChange) {
+ case QEvent::LanguageChange:
d->retranslateStrings();
+ break;
+
+ default: break;
}
+
return QWidget::event(event);
}
diff --git a/src/widgets/widgets/qdialogbuttonbox.h b/src/widgets/widgets/qdialogbuttonbox.h
index f6a0371d6c..d967494d0d 100644
--- a/src/widgets/widgets/qdialogbuttonbox.h
+++ b/src/widgets/widgets/qdialogbuttonbox.h
@@ -120,8 +120,6 @@ protected:
private:
Q_DISABLE_COPY(QDialogButtonBox)
Q_DECLARE_PRIVATE(QDialogButtonBox)
- Q_PRIVATE_SLOT(d_func(), void _q_handleButtonClicked())
- Q_PRIVATE_SLOT(d_func(), void _q_handleButtonDestroyed())
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QDialogButtonBox::StandardButtons)
diff --git a/src/widgets/widgets/qdialogbuttonbox_p.h b/src/widgets/widgets/qdialogbuttonbox_p.h
new file mode 100644
index 0000000000..c3d7e03489
--- /dev/null
+++ b/src/widgets/widgets/qdialogbuttonbox_p.h
@@ -0,0 +1,80 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QDIALOGBUTTONBOX_P_H
+#define QDIALOGBUTTONBOX_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qwidget_p.h>
+#include <qdialogbuttonbox.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_AUTOTEST_EXPORT QDialogButtonBoxPrivate : public QWidgetPrivate
+{
+ Q_DECLARE_PUBLIC(QDialogButtonBox)
+
+public:
+ enum class RemoveReason {
+ HideEvent,
+ ManualRemove,
+ Destroyed,
+ };
+ enum class LayoutRule {
+ DoLayout,
+ SkipLayout,
+ };
+ enum class AddRule {
+ Connect,
+ SkipConnect,
+ };
+
+ QDialogButtonBoxPrivate(Qt::Orientation orient);
+
+ QList<QAbstractButton *> buttonLists[QDialogButtonBox::NRoles];
+ QHash<QPushButton *, QDialogButtonBox::StandardButton> standardButtonHash;
+ QHash<QAbstractButton *, QDialogButtonBox::ButtonRole> hiddenButtons;
+
+ Qt::Orientation orientation;
+ QDialogButtonBox::ButtonLayout layoutPolicy;
+ QBoxLayout *buttonLayout;
+ std::unique_ptr<QObject> filter;
+ bool center;
+ bool ignoreShowAndHide = false;
+
+ void createStandardButtons(QDialogButtonBox::StandardButtons buttons);
+
+ void removeButton(QAbstractButton *button, RemoveReason reason);
+ void layoutButtons();
+ void initLayout();
+ void resetLayout();
+ QPushButton *createButton(QDialogButtonBox::StandardButton button,
+ LayoutRule layoutRule = LayoutRule::DoLayout);
+ void addButton(QAbstractButton *button, QDialogButtonBox::ButtonRole role,
+ LayoutRule layoutRule = LayoutRule::DoLayout,
+ AddRule addRule = AddRule::Connect);
+ void handleButtonDestroyed();
+ void handleButtonClicked();
+ bool handleButtonShowAndHide(QAbstractButton *button, QEvent *event);
+ void addButtonsToLayout(const QList<QAbstractButton *> &buttonList, bool reverse);
+ void ensureFirstAcceptIsDefault();
+ void retranslateStrings();
+ void disconnectAll();
+ QList<QAbstractButton *> allButtons() const;
+ QList<QAbstractButton *> visibleButtons() const;
+ QDialogButtonBox::ButtonRole buttonRole(QAbstractButton *button) const;
+};
+
+QT_END_NAMESPACE
+
+#endif // QDIALOGBUTTONBOX_P_H
diff --git a/src/widgets/widgets/qdockarealayout.cpp b/src/widgets/widgets/qdockarealayout.cpp
index 2e3602e47a..da0e987171 100644
--- a/src/widgets/widgets/qdockarealayout.cpp
+++ b/src/widgets/widgets/qdockarealayout.cpp
@@ -171,6 +171,38 @@ QDockAreaLayoutItem
return *this;
}
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QDockAreaLayoutItem *item)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace();
+ return item ? dbg << *item : dbg << "QDockAreaLayoutItem(0x0)";
+}
+
+QDebug operator<<(QDebug dbg, const QDockAreaLayoutItem &item)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace();
+ dbg << "QDockAreaLayoutItem(" << static_cast<const void *>(&item) << "->";
+ if (item.widgetItem) {
+ QWidget *widget = item.widgetItem->widget();
+ if (auto *dockWidget = qobject_cast<QDockWidget *>(widget)) {
+ dbg << "widgetItem(" << dockWidget << ")";
+ } else if (auto *groupWindow = qobject_cast<QDockWidgetGroupWindow *>(widget)) {
+ dbg << "widgetItem(" << groupWindow << "->(" << groupWindow->dockWidgets() << "))";
+ } else {
+ dbg << "widgetItem(" << widget << ")";
+ }
+ } else if (item.subinfo) {
+ dbg << "subInfo(" << item.subinfo << "->(" << item.subinfo->item_list << ")";
+ } else if (item.placeHolderItem) {
+ dbg << "placeHolderItem(" << item.placeHolderItem << ")";
+ }
+ dbg << ")";
+ return dbg;
+}
+#endif // QT_NO_DEBUG_STREAM
+
/******************************************************************************
** QDockAreaLayoutInfo
*/
@@ -986,6 +1018,14 @@ void QDockAreaLayoutInfo::remove(const QList<int> &path)
}
}
+void QDockAreaLayoutInfo::remove(QWidget *widget)
+{
+ const QList<int> path = indexOf(widget);
+ if (path.isEmpty())
+ return;
+ remove(path);
+}
+
QLayoutItem *QDockAreaLayoutInfo::plug(const QList<int> &path)
{
Q_ASSERT(!path.isEmpty());
@@ -1090,6 +1130,21 @@ static QRect dockedGeometry(QWidget *widget)
return result;
}
+bool QDockAreaLayoutInfo::hasGapItem(const QList<int> &path) const
+{
+ // empty path has no gap item
+ if (path.isEmpty())
+ return false;
+
+ // Index -1 isn't a gap
+ // Index out of range points at a position to be created. That isn't a gap either.
+ const int index = path.constFirst();
+ if (index < 0 || index >= item_list.count())
+ return false;
+
+ return item_list[index].flags & QDockAreaLayoutItem::GapItem;
+}
+
bool QDockAreaLayoutInfo::insertGap(const QList<int> &path, QLayoutItem *dockWidgetItem)
{
Q_ASSERT(!path.isEmpty());
@@ -1101,8 +1156,6 @@ bool QDockAreaLayoutInfo::insertGap(const QList<int> &path, QLayoutItem *dockWid
index = -index - 1;
}
-// dump(qDebug() << "insertGap() before:" << index << tabIndex, *this, QString());
-
if (path.size() > 1) {
QDockAreaLayoutItem &item = item_list[index];
@@ -1731,6 +1784,26 @@ QLayoutItem *QDockAreaLayoutInfo::takeAt(int *x, int index)
return nullptr;
}
+// Add a dock widget or dock widget group window to the item list
+void QDockAreaLayoutInfo::add(QWidget *widget)
+{
+ // Do not add twice
+ if (!indexOf(widget).isEmpty())
+ return;
+
+ if (auto *dockWidget = qobject_cast<QDockWidget *>(widget)) {
+ item_list.append(QDockAreaLayoutItem(new QDockWidgetItem(dockWidget)));
+ return;
+ }
+
+ if (auto *groupWindow = qobject_cast<QDockWidgetGroupWindow *>(widget)) {
+ item_list.append(QDockAreaLayoutItem(new QDockWidgetGroupWindowItem(groupWindow)));
+ return;
+ }
+
+ qFatal("Coding error. Add supports only QDockWidget and QDockWidgetGroupWindow");
+}
+
void QDockAreaLayoutInfo::deleteAllLayoutItems()
{
for (int i = 0; i < item_list.size(); ++i) {
@@ -1924,6 +1997,7 @@ bool QDockAreaLayoutInfo::restoreState(QDataStream &stream, QList<QDockWidget*>
if (testing) {
//was it is not really added to the layout, we need to delete the object here
delete item.widgetItem;
+ item.widgetItem = nullptr;
}
}
} else if (nextMarker == SequenceMarker) {
@@ -1989,14 +2063,20 @@ void QDockAreaLayoutInfo::updateSeparatorWidgets() const
break;
QWidget *sepWidget;
- if (j < separatorWidgets.size() && separatorWidgets.at(j)) {
+ if (j < separatorWidgets.size()) {
sepWidget = separatorWidgets.at(j);
+ if (!sepWidget) {
+ qWarning("QDockAreaLayoutInfo::updateSeparatorWidgets: null separator widget");
+ sepWidget = mainWindowLayout()->getSeparatorWidget();
+ separatorWidgets[j] = sepWidget;
+ }
} else {
sepWidget = mainWindowLayout()->getSeparatorWidget();
separatorWidgets.append(sepWidget);
}
j++;
+ Q_ASSERT(sepWidget);
sepWidget->raise();
QRect sepRect = separatorRect(i).adjusted(-2, -2, 2, 2);
@@ -2009,7 +2089,6 @@ void QDockAreaLayoutInfo::updateSeparatorWidgets() const
separatorWidgets[k]->hide();
}
separatorWidgets.resize(j);
- Q_ASSERT(separatorWidgets.size() == j);
}
/*! \internal
@@ -3271,12 +3350,18 @@ void QDockAreaLayout::updateSeparatorWidgets() const
QWidget *sepWidget;
if (j < separatorWidgets.size()) {
sepWidget = separatorWidgets.at(j);
+ if (!sepWidget) {
+ qWarning("QDockAreaLayout::updateSeparatorWidgets: null separator widget");
+ sepWidget = qt_mainwindow_layout(mainWindow)->getSeparatorWidget();
+ separatorWidgets[j] = sepWidget;
+ }
} else {
sepWidget = qt_mainwindow_layout(mainWindow)->getSeparatorWidget();
separatorWidgets.append(sepWidget);
}
j++;
+ Q_ASSERT(sepWidget);
sepWidget->raise();
QRect sepRect = separatorRect(i).adjusted(-2, -2, 2, 2);
diff --git a/src/widgets/widgets/qdockarealayout_p.h b/src/widgets/widgets/qdockarealayout_p.h
index cf998cf665..aa39bf9913 100644
--- a/src/widgets/widgets/qdockarealayout_p.h
+++ b/src/widgets/widgets/qdockarealayout_p.h
@@ -18,7 +18,6 @@
#include <QtWidgets/private/qtwidgetsglobal_p.h>
#include "QtWidgets/qlayout.h"
#include "QtCore/qlist.h"
-#include "QtCore/qpair.h"
#include "QtCore/qrect.h"
QT_REQUIRE_CONFIG(dockwidget);
@@ -73,6 +72,10 @@ struct Q_AUTOTEST_EXPORT QDockAreaLayoutItem
int pos;
int size;
uint flags;
+#ifndef QT_NO_DEBUG_STREAM
+ friend Q_AUTOTEST_EXPORT QDebug operator<<(QDebug dbg, const QDockAreaLayoutItem &item);
+ friend Q_AUTOTEST_EXPORT QDebug operator<<(QDebug dbg, const QDockAreaLayoutItem *item);
+#endif
};
class Q_AUTOTEST_EXPORT QPlaceHolderItem
@@ -105,6 +108,7 @@ public:
QList<int> gapIndex(const QPoint &pos, bool nestingEnabled,
TabMode tabMode) const;
void remove(const QList<int> &path);
+ void remove(QWidget *widget);
void unnest(int index);
void split(int index, Qt::Orientation orientation, QLayoutItem *dockWidgetItem);
#if QT_CONFIG(tabbar)
@@ -132,6 +136,7 @@ public:
void clear();
bool isEmpty() const;
+ bool hasGapItem(const QList<int> &path) const;
bool onlyHasPlaceholders() const;
bool hasFixedSize() const;
QList<int> findSeparator(const QPoint &pos) const;
@@ -151,6 +156,7 @@ public:
QLayoutItem *itemAt(int *x, int index) const;
QLayoutItem *takeAt(int *x, int index);
+ void add(QWidget *widget);
void deleteAllLayoutItems();
QMainWindowLayout *mainWindowLayout() const;
diff --git a/src/widgets/widgets/qdockwidget.cpp b/src/widgets/widgets/qdockwidget.cpp
index 67bb099446..706306000c 100644
--- a/src/widgets/widgets/qdockwidget.cpp
+++ b/src/widgets/widgets/qdockwidget.cpp
@@ -119,7 +119,7 @@ bool QDockWidgetTitleButton::event(QEvent *event)
{
switch (event->type()) {
case QEvent::StyleChange:
- case QEvent::ScreenChangeInternal:
+ case QEvent::DevicePixelRatioChange:
m_iconSize = -1;
break;
default:
@@ -128,33 +128,12 @@ bool QDockWidgetTitleButton::event(QEvent *event)
return QAbstractButton::event(event);
}
-static inline bool isWindowsStyle(const QStyle *style)
-{
- // Note: QStyleSheetStyle inherits QWindowsStyle
- const QStyle *effectiveStyle = style;
-
-#if QT_CONFIG(style_stylesheet)
- if (style->inherits("QStyleSheetStyle"))
- effectiveStyle = static_cast<const QStyleSheetStyle *>(style)->baseStyle();
-#endif
-#if !defined(QT_NO_STYLE_PROXY)
- if (style->inherits("QProxyStyle"))
- effectiveStyle = static_cast<const QProxyStyle *>(style)->baseStyle();
-#endif
-
- return effectiveStyle->inherits("QWindowsStyle");
-}
-
QSize QDockWidgetTitleButton::dockButtonIconSize() const
{
if (m_iconSize < 0) {
m_iconSize = style()->pixelMetric(QStyle::PM_SmallIconSize, nullptr, this);
- // Dock Widget title buttons on Windows where historically limited to size 10
- // (from small icon size 16) since only a 10x10 XPM was provided.
- // Adding larger pixmaps to the icons thus caused the icons to grow; limit
- // this to qpiScaled(10) here.
- if (isWindowsStyle(style()))
- m_iconSize = qMin((10 * logicalDpiX()) / 96, m_iconSize);
+ if (style()->styleHint(QStyle::SH_DockWidget_ButtonsHaveFrame, nullptr, this))
+ m_iconSize = (m_iconSize * 5) / 8; // 16 -> 10
}
return QSize(m_iconSize, m_iconSize);
}
@@ -253,7 +232,9 @@ bool QDockWidgetLayout::wmSupportsNativeWindowDeco()
return false;
#else
static const bool xcb = !QGuiApplication::platformName().compare("xcb"_L1, Qt::CaseInsensitive);
- return !xcb;
+ static const bool wayland =
+ QGuiApplication::platformName().startsWith("wayland"_L1, Qt::CaseInsensitive);
+ return !(xcb || wayland);
#endif
}
@@ -627,12 +608,13 @@ void QDockWidgetPrivate::init()
QAbstractButton *button = new QDockWidgetTitleButton(q);
button->setObjectName("qt_dockwidget_floatbutton"_L1);
- QObject::connect(button, SIGNAL(clicked()), q, SLOT(_q_toggleTopLevel()));
+ QObjectPrivate::connect(button, &QAbstractButton::clicked,
+ this, &QDockWidgetPrivate::toggleTopLevel);
layout->setWidgetForRole(QDockWidgetLayout::FloatButton, button);
button = new QDockWidgetTitleButton(q);
button->setObjectName("qt_dockwidget_closebutton"_L1);
- QObject::connect(button, SIGNAL(clicked()), q, SLOT(close()));
+ QObject::connect(button, &QAbstractButton::clicked, q, &QDockWidget::close);
layout->setWidgetForRole(QDockWidgetLayout::CloseButton, button);
font = QApplication::font("QDockWidgetTitle");
@@ -643,8 +625,8 @@ void QDockWidgetPrivate::init()
toggleViewAction->setMenuRole(QAction::NoRole);
fixedWindowTitle = qt_setWindowTitle_helperHelper(q->windowTitle(), q);
toggleViewAction->setText(fixedWindowTitle);
- QObject::connect(toggleViewAction, SIGNAL(triggered(bool)),
- q, SLOT(_q_toggleView(bool)));
+ QObjectPrivate::connect(toggleViewAction, &QAction::triggered,
+ this, &QDockWidgetPrivate::toggleView);
#endif
updateButtons();
@@ -679,7 +661,7 @@ void QDockWidget::initStyleOption(QStyleOptionDockWidget *option) const
option->verticalTitleBar = l->verticalTitleBar;
}
-void QDockWidgetPrivate::_q_toggleView(bool b)
+void QDockWidgetPrivate::toggleView(bool b)
{
Q_Q(QDockWidget);
if (b == q->isHidden()) {
@@ -727,7 +709,7 @@ void QDockWidgetPrivate::updateButtons()
layout->invalidate();
}
-void QDockWidgetPrivate::_q_toggleTopLevel()
+void QDockWidgetPrivate::toggleTopLevel()
{
Q_Q(QDockWidget);
q->setFloating(!q->isFloating());
@@ -768,7 +750,7 @@ void QDockWidgetPrivate::initDrag(const QPoint &pos, bool nca)
tabbed widgets, and false if the dock widget should always be dragged
alone.
*/
-void QDockWidgetPrivate::startDrag(bool group)
+void QDockWidgetPrivate::startDrag(DragScope scope)
{
Q_Q(QDockWidget);
@@ -778,7 +760,11 @@ void QDockWidgetPrivate::startDrag(bool group)
QMainWindowLayout *layout = qt_mainwindow_layout_from_dock(q);
Q_ASSERT(layout != nullptr);
- state->widgetItem = layout->unplug(q, group);
+#if QT_CONFIG(draganddrop)
+ bool wasFloating = q->isFloating();
+#endif
+
+ state->widgetItem = layout->unplug(q, scope);
if (state->widgetItem == nullptr) {
/* Dock widget has a QMainWindow parent, but was never inserted with
QMainWindow::addDockWidget, so the QMainWindowLayout has no
@@ -796,6 +782,20 @@ void QDockWidgetPrivate::startDrag(bool group)
layout->restore();
state->dragging = true;
+
+#if QT_CONFIG(draganddrop)
+ if (QMainWindowLayout::needsPlatformDrag()) {
+ Qt::DropAction result =
+ layout->performPlatformWidgetDrag(state->widgetItem, state->pressPos);
+ if (result == Qt::IgnoreAction && !wasFloating) {
+ layout->revert(state->widgetItem);
+ delete state;
+ state = nullptr;
+ } else {
+ endDrag(QDockWidgetPrivate::EndDragMode::LocationChange);
+ }
+ }
+#endif
}
/*! \internal
@@ -803,7 +803,7 @@ void QDockWidgetPrivate::startDrag(bool group)
The \a abort parameter specifies that it ends because of programmatic state
reset rather than mouse release event.
*/
-void QDockWidgetPrivate::endDrag(bool abort)
+void QDockWidgetPrivate::endDrag(EndDragMode mode)
{
Q_Q(QDockWidget);
Q_ASSERT(state != nullptr);
@@ -815,7 +815,11 @@ void QDockWidgetPrivate::endDrag(bool abort)
Q_ASSERT(mainWindow != nullptr);
QMainWindowLayout *mwLayout = qt_mainwindow_layout(mainWindow);
- if (abort || !mwLayout->plug(state->widgetItem)) {
+ // if mainWindow is being deleted in an ongoing drag, make it a no-op instead of crashing
+ if (!mwLayout)
+ return;
+
+ if (mode == EndDragMode::Abort || !mwLayout->plug(state->widgetItem)) {
if (hasFeature(this, QDockWidget::DockWidgetFloatable)) {
// This QDockWidget will now stay in the floating state.
if (state->ownWidgetItem) {
@@ -847,6 +851,11 @@ void QDockWidgetPrivate::endDrag(bool abort)
tabPosition = mwLayout->tabPosition(toDockWidgetArea(dwgw->layoutInfo()->dockPos));
}
#endif
+ // Reparent, if the drag was out of a dock widget group window
+ if (mode == EndDragMode::LocationChange) {
+ if (auto *groupWindow = qobject_cast<QDockWidgetGroupWindow *>(q->parentWidget()))
+ groupWindow->reparent(q);
+ }
}
q->activateWindow();
} else {
@@ -875,6 +884,10 @@ Qt::DockWidgetArea QDockWidgetPrivate::toDockWidgetArea(QInternal::DockPosition
void QDockWidgetPrivate::setResizerActive(bool active)
{
Q_Q(QDockWidget);
+ const auto *dwLayout = qobject_cast<QDockWidgetLayout *>(layout);
+ if (dwLayout->nativeWindowDeco(q->isFloating()))
+ return;
+
if (active && !resizer)
resizer = new QWidgetResizeHandler(q);
if (resizer)
@@ -937,13 +950,22 @@ bool QDockWidgetPrivate::mouseDoubleClickEvent(QMouseEvent *event)
if (event->button() == Qt::LeftButton && titleArea.contains(event->position().toPoint()) &&
hasFeature(this, QDockWidget::DockWidgetFloatable)) {
- _q_toggleTopLevel();
+ toggleTopLevel();
return true;
}
}
return false;
}
+bool QDockWidgetPrivate::isTabbed() const
+{
+ Q_Q(const QDockWidget);
+ QDockWidget *that = const_cast<QDockWidget *>(q);
+ auto *mwLayout = qt_mainwindow_layout_from_dock(that);
+ Q_ASSERT(mwLayout);
+ return mwLayout->isDockWidgetTabbed(q);
+}
+
bool QDockWidgetPrivate::mouseMoveEvent(QMouseEvent *event)
{
bool ret = false;
@@ -974,7 +996,8 @@ bool QDockWidgetPrivate::mouseMoveEvent(QMouseEvent *event)
} else
#endif
{
- startDrag();
+ const DragScope scope = isTabbed() ? DragScope::Group : DragScope::Widget;
+ startDrag(scope);
q->grabMouse();
ret = true;
}
@@ -1038,9 +1061,15 @@ bool QDockWidgetPrivate::mouseMoveEvent(QMouseEvent *event)
bool QDockWidgetPrivate::mouseReleaseEvent(QMouseEvent *event)
{
#if QT_CONFIG(mainwindow)
+#if QT_CONFIG(draganddrop)
+ // if we are peforming a platform drag ignore the release here and end the drag when the actual
+ // drag ends.
+ if (QMainWindowLayout::needsPlatformDrag())
+ return false;
+#endif
if (event->button() == Qt::LeftButton && state && !state->nca) {
- endDrag();
+ endDrag(EndDragMode::LocationChange);
return true; //filter out the event
}
@@ -1079,26 +1108,25 @@ void QDockWidgetPrivate::nonClientAreaMouseEvent(QMouseEvent *event)
break;
state->ctrlDrag = (event->modifiers() & Qt::ControlModifier) ||
(!hasFeature(this, QDockWidget::DockWidgetMovable) && q->isFloating());
- startDrag();
+ startDrag(DragScope::Group);
break;
case QEvent::NonClientAreaMouseMove:
if (state == nullptr || !state->dragging)
break;
#if !defined(Q_OS_MAC) && !defined(Q_OS_WASM)
- if (state->nca) {
- endDrag();
- }
+ if (state->nca)
+ endDrag(EndDragMode::LocationChange);
#endif
break;
case QEvent::NonClientAreaMouseButtonRelease:
#if defined(Q_OS_MAC) || defined(Q_OS_WASM)
if (state)
- endDrag();
+ endDrag(EndDragMode::LocationChange);
#endif
break;
case QEvent::NonClientAreaMouseButtonDblClick:
- _q_toggleTopLevel();
+ toggleTopLevel();
break;
default:
break;
@@ -1164,10 +1192,10 @@ void QDockWidgetPrivate::setWindowState(bool floating, bool unplug, const QRect
return; // this dockwidget can't be redocked
}
- bool wasFloating = q->isFloating();
+ const bool wasFloating = q->isFloating();
if (wasFloating) // Prevent repetitive unplugging from nested invocations (QTBUG-42818)
unplug = false;
- bool hidden = q->isHidden();
+ const bool hidden = q->isHidden();
if (q->isVisible())
q->hide();
@@ -1185,8 +1213,12 @@ void QDockWidgetPrivate::setWindowState(bool floating, bool unplug, const QRect
flags |= Qt::FramelessWindowHint;
}
- if (unplug)
+#if QT_CONFIG(draganddrop)
+ // If we are performing a platform drag the flag is not needed and we want to avoid recreating
+ // the platform window when it would be removed later
+ if (unplug && !QMainWindowLayout::needsPlatformDrag())
flags |= Qt::X11BypassWindowManagerHint;
+#endif
q->setWindowFlags(flags);
@@ -1262,15 +1294,13 @@ void QDockWidgetPrivate::setWindowState(bool floating, bool unplug, const QRect
possible to drag the dock widget when undocking. Starting the drag will undock
the dock widget, but a second drag will be needed to move the dock widget itself.
- \sa QMainWindow, {Dock Widgets Example}
+ \sa QMainWindow
*/
/*!
\enum QDockWidget::DockWidgetFeature
- \value DockWidgetClosable The dock widget can be closed. On some systems the dock
- widget always has a close button when it's floating
- (for example on MacOS 10.5).
+ \value DockWidgetClosable The dock widget can be closed.
\value DockWidgetMovable The dock widget can be moved between docks
by the user.
\value DockWidgetFloatable The dock widget can be detached from the
@@ -1402,9 +1432,12 @@ QDockWidget::DockWidgetFeatures QDockWidget::features() const
\property QDockWidget::floating
\brief whether the dock widget is floating
- A floating dock widget is presented to the user as an independent
- window "on top" of its parent QMainWindow, instead of being
- docked in the QMainWindow.
+ A floating dock widget is presented to the user as a single, independent
+ window "on top" of its parent QMainWindow, instead of being docked
+ either in the QMainWindow, or in a group of tabbed dock widgets.
+
+ Floating dock widgets can be individually positioned and resized, both
+ programmatically or by mouse interaction.
By default, this property is \c true.
@@ -1418,7 +1451,7 @@ void QDockWidget::setFloating(bool floating)
// the initial click of a double-click may have started a drag...
if (d->state != nullptr)
- d->endDrag(true);
+ d->endDrag(QDockWidgetPrivate::EndDragMode::Abort);
QRect r = d->undockedGeometry;
// Keep position when undocking for the first time.
@@ -1473,8 +1506,14 @@ void QDockWidget::changeEvent(QEvent *event)
QDockWidgetLayout *layout = qobject_cast<QDockWidgetLayout*>(this->layout());
switch (event->type()) {
- case QEvent::ModifiedChange:
case QEvent::WindowTitleChange:
+ if (isFloating() && windowHandle() && d->topData() && windowHandle()->isVisible()) {
+ // From QWidget::setWindowTitle(): Propagate window title without signal emission
+ d->topData()->caption = windowHandle()->title();
+ d->setWindowTitle_helper(windowHandle()->title());
+ }
+ Q_FALLTHROUGH();
+ case QEvent::ModifiedChange:
update(layout->titleArea());
#ifndef QT_NO_ACTION
d->fixedWindowTitle = qt_setWindowTitle_helperHelper(windowTitle(), this);
@@ -1500,8 +1539,14 @@ void QDockWidget::closeEvent(QCloseEvent *event)
{
Q_D(QDockWidget);
if (d->state)
- d->endDrag(true);
- QWidget::closeEvent(event);
+ d->endDrag(QDockWidgetPrivate::EndDragMode::Abort);
+
+ // For non-closable widgets, don't allow closing, except when the mainwindow
+ // is hidden, as otherwise an application wouldn't be able to be shut down.
+ const QMainWindow *win = qobject_cast<QMainWindow*>(parentWidget());
+ const bool canClose = (d->features & DockWidgetClosable)
+ || (!win || !win->isVisible());
+ event->setAccepted(canClose);
}
/*! \reimp */
@@ -1691,6 +1736,10 @@ QAction * QDockWidget::toggleViewAction() const
invisible). This happens when the widget is hidden or shown, as
well as when it is docked in a tabbed dock area and its tab
becomes selected or unselected.
+
+ \note The signal can differ from QWidget::isVisible(). This can be the case, if
+ a dock widget is minimized or tabified and associated to a non-selected or
+ inactive tab.
*/
/*!
@@ -1772,6 +1821,26 @@ QWidget *QDockWidget::titleBarWidget() const
return layout->widgetForRole(QDockWidgetLayout::TitleBar);
}
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug dbg, const QDockWidget *dockWidget)
+{
+ QDebugStateSaver saver(dbg);
+ dbg.nospace();
+
+ if (!dockWidget) {
+ dbg << "QDockWidget(0x0)";
+ return dbg;
+ }
+
+ dbg << "QDockWidget(" << static_cast<const void *>(dockWidget);
+ dbg << "->(ObjectName=" << dockWidget->objectName();
+ dbg << "; floating=" << dockWidget->isFloating();
+ dbg << "; features=" << dockWidget->features();
+ dbg << ";))";
+ return dbg;
+}
+#endif // QT_NO_DEBUG_STREAM
+
QT_END_NAMESPACE
#include "qdockwidget.moc"
diff --git a/src/widgets/widgets/qdockwidget.h b/src/widgets/widgets/qdockwidget.h
index f524a88423..2efa1d3c2f 100644
--- a/src/widgets/widgets/qdockwidget.h
+++ b/src/widgets/widgets/qdockwidget.h
@@ -20,7 +20,7 @@ class Q_WIDGETS_EXPORT QDockWidget : public QWidget
{
Q_OBJECT
- Q_PROPERTY(bool floating READ isFloating WRITE setFloating)
+ Q_PROPERTY(bool floating READ isFloating WRITE setFloating NOTIFY topLevelChanged)
Q_PROPERTY(DockWidgetFeatures features READ features WRITE setFeatures NOTIFY featuresChanged)
Q_PROPERTY(Qt::DockWidgetAreas allowedAreas READ allowedAreas
WRITE setAllowedAreas NOTIFY allowedAreasChanged)
@@ -64,6 +64,10 @@ public:
inline bool isAreaAllowed(Qt::DockWidgetArea area) const
{ return (allowedAreas() & area) == area; }
+#ifndef QT_NO_DEBUG_STREAM
+ friend Q_WIDGETS_EXPORT QDebug operator<<(QDebug dbg, const QDockWidget *dockWidget);
+#endif
+
#ifndef QT_NO_ACTION
QAction *toggleViewAction() const;
#endif
@@ -72,7 +76,7 @@ Q_SIGNALS:
void featuresChanged(QDockWidget::DockWidgetFeatures features);
void topLevelChanged(bool topLevel);
void allowedAreasChanged(Qt::DockWidgetAreas allowedAreas);
- void visibilityChanged(bool visible);
+ void visibilityChanged(bool visible); // ### Qt7: Deprecate this. Better listen to hide/show events
void dockLocationChanged(Qt::DockWidgetArea area);
protected:
@@ -85,8 +89,6 @@ protected:
private:
Q_DECLARE_PRIVATE(QDockWidget)
Q_DISABLE_COPY(QDockWidget)
- Q_PRIVATE_SLOT(d_func(), void _q_toggleView(bool))
- Q_PRIVATE_SLOT(d_func(), void _q_toggleTopLevel())
friend class QDockAreaLayout;
friend class QDockWidgetItem;
friend class QMainWindowLayout;
diff --git a/src/widgets/widgets/qdockwidget_p.h b/src/widgets/widgets/qdockwidget_p.h
index 326c55cc78..fa936599c6 100644
--- a/src/widgets/widgets/qdockwidget_p.h
+++ b/src/widgets/widgets/qdockwidget_p.h
@@ -51,9 +51,19 @@ class QDockWidgetPrivate : public QWidgetPrivate
};
public:
+ enum class DragScope {
+ Group,
+ Widget
+ };
+
+ enum class EndDragMode {
+ LocationChange,
+ Abort
+ };
+
void init();
- void _q_toggleView(bool); // private slot
- void _q_toggleTopLevel(); // private slot
+ void toggleView(bool);
+ void toggleTopLevel();
void updateButtons();
static Qt::DockWidgetArea toDockWidgetArea(QInternal::DockPosition pos);
@@ -86,8 +96,8 @@ public:
void setWindowState(bool floating, bool unplug = false, const QRect &rect = QRect());
void nonClientAreaMouseEvent(QMouseEvent *event);
void initDrag(const QPoint &pos, bool nca);
- void startDrag(bool group = true);
- void endDrag(bool abort = false);
+ void startDrag(DragScope scope);
+ void endDrag(EndDragMode mode);
void moveEvent(QMoveEvent *event);
void recalculatePressPos(QResizeEvent *event);
@@ -96,6 +106,7 @@ public:
void setResizerActive(bool active);
bool isAnimating() const;
+ bool isTabbed() const;
private:
QWidgetResizeHandler *resizer = nullptr;
diff --git a/src/widgets/widgets/qeffects.cpp b/src/widgets/widgets/qeffects.cpp
index eaf8deca69..74ac24a2b0 100644
--- a/src/widgets/widgets/qeffects.cpp
+++ b/src/widgets/widgets/qeffects.cpp
@@ -115,7 +115,7 @@ void QAlphaWidget::run(int time)
qApp->installEventFilter(this);
widget->setWindowOpacity(0.0);
widget->show();
- connect(&anim, SIGNAL(timeout()), this, SLOT(render()));
+ connect(&anim, &QTimer::timeout, this, &QAlphaWidget::render);
anim.start(1);
#else
//This is roughly equivalent to calling setVisible(true) without actually showing the widget
@@ -138,7 +138,7 @@ void QAlphaWidget::run(int time)
show();
setEnabled(false);
- connect(&anim, SIGNAL(timeout()), this, SLOT(render()));
+ connect(&anim, &QTimer::timeout, this, &QAlphaWidget::render);
anim.start(1);
} else {
duration = 0;
@@ -292,6 +292,7 @@ void QAlphaWidget::alphaBlend()
back_data += bpl;
front_data += bpl;
}
+ break;
}
default:
break;
@@ -428,7 +429,7 @@ void QRollEffect::run(int time)
duration = qMin(qMax(dist/3, 50), 120);
}
- connect(&anim, SIGNAL(timeout()), this, SLOT(scroll()));
+ connect(&anim, &QTimer::timeout, this, &QRollEffect::scroll);
move(widget->geometry().x(),widget->geometry().y());
resize(qMin(currentWidth, totalWidth), qMin(currentHeight, totalHeight));
diff --git a/src/widgets/widgets/qfocusframe.cpp b/src/widgets/widgets/qfocusframe.cpp
index 396f7fca14..d66faedbac 100644
--- a/src/widgets/widgets/qfocusframe.cpp
+++ b/src/widgets/widgets/qfocusframe.cpp
@@ -52,8 +52,8 @@ void QFocusFramePrivate::updateSize()
QStyleOption opt;
q->initStyleOption(&opt);
- int vmargin = q->style()->pixelMetric(QStyle::PM_FocusFrameVMargin, &opt),
- hmargin = q->style()->pixelMetric(QStyle::PM_FocusFrameHMargin, &opt);
+ int vmargin = q->style()->pixelMetric(QStyle::PM_FocusFrameVMargin, &opt, q),
+ hmargin = q->style()->pixelMetric(QStyle::PM_FocusFrameHMargin, &opt, q);
QPoint pos(widget->x(), widget->y());
if (q->parentWidget() != widget->parentWidget())
pos = widget->parentWidget()->mapTo(q->parentWidget(), pos);
@@ -229,8 +229,8 @@ QFocusFrame::paintEvent(QPaintEvent *)
QStylePainter p(this);
QStyleOption option;
initStyleOption(&option);
- const int vmargin = style()->pixelMetric(QStyle::PM_FocusFrameVMargin, &option);
- const int hmargin = style()->pixelMetric(QStyle::PM_FocusFrameHMargin, &option);
+ const int vmargin = style()->pixelMetric(QStyle::PM_FocusFrameVMargin, &option, this);
+ const int hmargin = style()->pixelMetric(QStyle::PM_FocusFrameHMargin, &option, this);
QWidgetPrivate *wd = qt_widget_private(d->widget);
QRect rect = wd->clipRect().adjusted(0, 0, hmargin*2, vmargin*2);
p.setClipRect(rect);
diff --git a/src/widgets/widgets/qfontcombobox.cpp b/src/widgets/widgets/qfontcombobox.cpp
index a7f31995e1..c80687c7ed 100644
--- a/src/widgets/widgets/qfontcombobox.cpp
+++ b/src/widgets/widgets/qfontcombobox.cpp
@@ -3,8 +3,9 @@
#include "qfontcombobox.h"
+#include <qabstractitemdelegate.h>
+#include <qaccessible.h>
#include <qstringlistmodel.h>
-#include <qitemdelegate.h>
#include <qlistview.h>
#include <qpainter.h>
#include <qevent.h>
@@ -12,6 +13,8 @@
#include <private/qcombobox_p.h>
#include <qdebug.h>
+#include <array>
+
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
@@ -100,7 +103,7 @@ static QFontDatabase::WritingSystem writingSystemFromLocale()
static QFontDatabase::WritingSystem writingSystemForFont(const QFont &font, bool *hasLatin)
{
- QList<QFontDatabase::WritingSystem> writingSystems = QFontDatabase::writingSystems(font.families().first());
+ QList<QFontDatabase::WritingSystem> writingSystems = QFontDatabase::writingSystems(font.families().constFirst());
// qDebug() << font.families().first() << writingSystems;
// this just confuses the algorithm below. Vietnamese is Latin with lots of special chars
@@ -147,16 +150,15 @@ static QFontDatabase::WritingSystem writingSystemForFont(const QFont &font, bool
class QFontComboBoxPrivate : public QComboBoxPrivate
{
public:
- inline QFontComboBoxPrivate() { filters = QFontComboBox::AllFonts; }
-
- QFontComboBox::FontFilters filters;
+ QFontComboBox::FontFilters filters = QFontComboBox::AllFonts;
QFont currentFont;
QHash<QFontDatabase::WritingSystem, QString> sampleTextForWritingSystem;
QHash<QString, QString> sampleTextForFontFamily;
QHash<QString, QFont> displayFontForFontFamily;
+ std::array<QMetaObject::Connection, 2> connections;
- void _q_updateModel();
- void _q_currentChanged(const QString &);
+ void updateModel();
+ void currentChanged(const QString &);
Q_DECLARE_PUBLIC(QFontComboBox)
};
@@ -281,7 +283,7 @@ QSize QFontFamilyDelegate::sizeHint(const QStyleOptionViewItem &option,
}
-void QFontComboBoxPrivate::_q_updateModel()
+void QFontComboBoxPrivate::updateModel()
{
Q_Q(QFontComboBox);
@@ -297,29 +299,28 @@ void QFontComboBoxPrivate::_q_updateModel()
QFontFamilyDelegate *delegate = qobject_cast<QFontFamilyDelegate *>(q->view()->itemDelegate());
QFontDatabase::WritingSystem system = delegate ? delegate->writingSystem : QFontDatabase::Any;
- QStringList list = QFontDatabase::families(system);
+ const QStringList list = QFontDatabase::families(system);
QStringList result;
int offset = 0;
QFontInfo fi(currentFont);
- for (int i = 0; i < list.size(); ++i) {
- if (QFontDatabase::isPrivateFamily(list.at(i)))
+ for (const auto &family : list) {
+ if (QFontDatabase::isPrivateFamily(family))
continue;
if ((filters & scalableMask) && (filters & scalableMask) != scalableMask) {
- if (bool(filters & QFontComboBox::ScalableFonts) != QFontDatabase::isSmoothlyScalable(list.at(i)))
+ if (bool(filters & QFontComboBox::ScalableFonts) != QFontDatabase::isSmoothlyScalable(family))
continue;
}
if ((filters & spacingMask) && (filters & spacingMask) != spacingMask) {
- if (bool(filters & QFontComboBox::MonospacedFonts) != QFontDatabase::isFixedPitch(list.at(i)))
+ if (bool(filters & QFontComboBox::MonospacedFonts) != QFontDatabase::isFixedPitch(family))
continue;
}
- result += list.at(i);
- if (list.at(i) == fi.family() || list.at(i).startsWith(fi.family() + " ["_L1))
+ result += family;
+ if (family == fi.family() || family.startsWith(fi.family() + " ["_L1))
offset = result.size() - 1;
}
- list = result;
//we need to block the signals so that the model doesn't emit reset
//this prevents the current index from changing
@@ -327,10 +328,17 @@ void QFontComboBoxPrivate::_q_updateModel()
///TODO: we should finda way to avoid blocking signals and have a real update of the model
{
const QSignalBlocker blocker(m);
- m->setStringList(list);
+ m->setStringList(result);
+ // Since the modelReset signal is blocked the view will not emit an accessibility event
+ #if QT_CONFIG(accessibility)
+ if (QAccessible::isActive()) {
+ QAccessibleTableModelChangeEvent accessibleEvent(q->view(), QAccessibleTableModelChangeEvent::ModelReset);
+ QAccessible::updateAccessibility(&accessibleEvent);
+ }
+ #endif
}
- if (list.isEmpty()) {
+ if (result.isEmpty()) {
if (currentFont != QFont()) {
currentFont = QFont();
emit q->currentFontChanged(currentFont);
@@ -341,10 +349,10 @@ void QFontComboBoxPrivate::_q_updateModel()
}
-void QFontComboBoxPrivate::_q_currentChanged(const QString &text)
+void QFontComboBoxPrivate::currentChanged(const QString &text)
{
Q_Q(QFontComboBox);
- QStringList families = currentFont.families();
+ const QStringList families = currentFont.families();
if (families.isEmpty() || families.first() != text) {
currentFont.setFamilies(QStringList{text});
emit q->currentFontChanged(currentFont);
@@ -381,7 +389,7 @@ void QFontComboBoxPrivate::_q_currentChanged(const QString &text)
\image windowsvista-fontcombobox.png Screenshot of QFontComboBox on Windows Vista
- \sa QComboBox, QFont, QFontInfo, QFontMetrics, QFontDatabase, {Character Map Example}
+ \sa QComboBox, QFont, QFontInfo, QFontMetrics, QFontDatabase
*/
/*!
@@ -402,11 +410,12 @@ QFontComboBox::QFontComboBox(QWidget *parent)
lview->setUniformItemSizes(true);
setWritingSystem(QFontDatabase::Any);
- connect(this, SIGNAL(currentTextChanged(QString)),
- this, SLOT(_q_currentChanged(QString)));
-
- connect(qApp, SIGNAL(fontDatabaseChanged()),
- this, SLOT(_q_updateModel()));
+ d->connections = {
+ QObjectPrivate::connect(this, &QFontComboBox::currentTextChanged,
+ d, &QFontComboBoxPrivate::currentChanged),
+ QObjectPrivate::connect(qApp, &QGuiApplication::fontDatabaseChanged,
+ d, &QFontComboBoxPrivate::updateModel),
+ };
}
@@ -415,6 +424,9 @@ QFontComboBox::QFontComboBox(QWidget *parent)
*/
QFontComboBox::~QFontComboBox()
{
+ Q_D(const QFontComboBox);
+ for (const QMetaObject::Connection &connection : d->connections)
+ QObject::disconnect(connection);
}
/*!
@@ -433,7 +445,7 @@ void QFontComboBox::setWritingSystem(QFontDatabase::WritingSystem script)
QFontFamilyDelegate *delegate = qobject_cast<QFontFamilyDelegate *>(view()->itemDelegate());
if (delegate)
delegate->writingSystem = script;
- d->_q_updateModel();
+ d->updateModel();
}
QFontDatabase::WritingSystem QFontComboBox::writingSystem() const
@@ -469,7 +481,7 @@ void QFontComboBox::setFontFilters(FontFilters filters)
{
Q_D(QFontComboBox);
d->filters = filters;
- d->_q_updateModel();
+ d->updateModel();
}
QFontComboBox::FontFilters QFontComboBox::fontFilters() const
@@ -495,8 +507,8 @@ void QFontComboBox::setCurrentFont(const QFont &font)
Q_D(QFontComboBox);
if (font != d->currentFont) {
d->currentFont = font;
- d->_q_updateModel();
- if (d->currentFont == font) { //else the signal has already be emitted by _q_updateModel
+ d->updateModel();
+ if (d->currentFont == font) { //else the signal has already be emitted by updateModel
emit currentFontChanged(d->currentFont);
}
}
diff --git a/src/widgets/widgets/qfontcombobox.h b/src/widgets/widgets/qfontcombobox.h
index 8c4bad2ae0..9c5432b332 100644
--- a/src/widgets/widgets/qfontcombobox.h
+++ b/src/widgets/widgets/qfontcombobox.h
@@ -65,8 +65,6 @@ protected:
private:
Q_DISABLE_COPY(QFontComboBox)
Q_DECLARE_PRIVATE(QFontComboBox)
- Q_PRIVATE_SLOT(d_func(), void _q_currentChanged(const QString &))
- Q_PRIVATE_SLOT(d_func(), void _q_updateModel())
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QFontComboBox::FontFilters)
diff --git a/src/widgets/widgets/qgroupbox.cpp b/src/widgets/widgets/qgroupbox.cpp
index 8fd90f8b77..eb37599382 100644
--- a/src/widgets/widgets/qgroupbox.cpp
+++ b/src/widgets/widgets/qgroupbox.cpp
@@ -22,6 +22,7 @@
#include <qpa/qplatformtheme.h>
#include "qdebug.h"
+#include <QtCore/qpointer.h>
QT_BEGIN_NAMESPACE
@@ -132,9 +133,9 @@ void QGroupBoxPrivate::click()
widgets). The following example shows how we can set up a
QGroupBox with a layout:
- \snippet widgets/groupbox/window.cpp 2
+ \snippet code/src_gui_widgets_qgroupbox.cpp Set up QGroupBox with layout
- \sa QButtonGroup, {Group Box Example}
+ \sa QButtonGroup
*/
@@ -463,9 +464,9 @@ QSize QGroupBox::minimumSizeHint() const
int baseWidth = metrics.horizontalAdvance(d->title) + metrics.horizontalAdvance(u' ');
int baseHeight = metrics.height();
if (d->checkable) {
- baseWidth += style()->pixelMetric(QStyle::PM_IndicatorWidth, &option);
- baseWidth += style()->pixelMetric(QStyle::PM_CheckBoxLabelSpacing, &option);
- baseHeight = qMax(baseHeight, style()->pixelMetric(QStyle::PM_IndicatorHeight, &option));
+ baseWidth += style()->pixelMetric(QStyle::PM_IndicatorWidth, &option, this);
+ baseWidth += style()->pixelMetric(QStyle::PM_CheckBoxLabelSpacing, &option, this);
+ baseHeight = qMax(baseHeight, style()->pixelMetric(QStyle::PM_IndicatorHeight, &option, this));
}
QSize size = style()->sizeFromContents(QStyle::CT_GroupBox, &option, QSize(baseWidth, baseHeight), this);
diff --git a/src/widgets/widgets/qkeysequenceedit.cpp b/src/widgets/widgets/qkeysequenceedit.cpp
index 9a9bf25668..65ed7a465a 100644
--- a/src/widgets/widgets/qkeysequenceedit.cpp
+++ b/src/widgets/widgets/qkeysequenceedit.cpp
@@ -20,7 +20,7 @@ void QKeySequenceEditPrivate::init()
lineEdit = new QLineEdit(q);
lineEdit->setObjectName(QStringLiteral("qt_keysequenceedit_lineedit"));
lineEdit->setClearButtonEnabled(false);
- q->connect(lineEdit, &QLineEdit::textChanged, [q](const QString& text) {
+ q->connect(lineEdit, &QLineEdit::textChanged, q, [q](const QString& text) {
// Clear the shortcut if the user clicked on the clear icon
if (text.isEmpty())
q->clear();
@@ -344,22 +344,23 @@ void QKeySequenceEdit::keyPressEvent(QKeyEvent *e)
return;
if (e->modifiers() & Qt::ShiftModifier) {
- QList<int> possibleKeys = QKeyMapper::possibleKeys(e);
+ const QList<QKeyCombination> possibleKeys = QKeyMapper::possibleKeys(e);
int pkTotal = possibleKeys.size();
if (!pkTotal)
return;
bool found = false;
for (int i = 0; i < possibleKeys.size(); ++i) {
- if (possibleKeys.at(i) - nextKey == int(e->modifiers())
- || (possibleKeys.at(i) == nextKey && e->modifiers() == Qt::ShiftModifier)) {
- nextKey = possibleKeys.at(i);
+ const int key = possibleKeys.at(i).toCombined();
+ if (key - nextKey == int(e->modifiers())
+ || (key == nextKey && e->modifiers() == Qt::ShiftModifier)) {
+ nextKey = key;
found = true;
break;
}
}
// Use as fallback
if (!found)
- nextKey = possibleKeys.first();
+ nextKey = possibleKeys.first().toCombined();
} else {
nextKey |= d->translateModifiers(e->modifiers(), e->text());
}
@@ -414,7 +415,8 @@ void QKeySequenceEdit::timerEvent(QTimerEvent *e)
void QKeySequenceEdit::focusOutEvent(QFocusEvent *e)
{
Q_D(QKeySequenceEdit);
- d->finishEditing();
+ if (e->reason() != Qt::PopupFocusReason)
+ d->finishEditing();
QWidget::focusOutEvent(e);
}
diff --git a/src/widgets/widgets/qlabel.cpp b/src/widgets/widgets/qlabel.cpp
index 4d1c4ebb74..f4155de0a0 100644
--- a/src/widgets/widgets/qlabel.cpp
+++ b/src/widgets/widgets/qlabel.cpp
@@ -1171,12 +1171,14 @@ void QLabel::setBuddy(QWidget *buddy)
Q_D(QLabel);
if (d->buddy)
- disconnect(d->buddy, SIGNAL(destroyed()), this, SLOT(_q_buddyDeleted()));
+ QObjectPrivate::disconnect(d->buddy, &QObject::destroyed,
+ d, &QLabelPrivate::buddyDeleted);
d->buddy = buddy;
if (buddy)
- connect(buddy, SIGNAL(destroyed()), this, SLOT(_q_buddyDeleted()));
+ QObjectPrivate::connect(buddy, &QObject::destroyed,
+ d, &QLabelPrivate::buddyDeleted);
if (d->isTextLabel) {
if (d->shortcutId)
@@ -1219,7 +1221,7 @@ void QLabelPrivate::updateShortcut()
}
-void QLabelPrivate::_q_buddyDeleted()
+void QLabelPrivate::buddyDeleted()
{
Q_Q(QLabel);
q->setBuddy(nullptr);
@@ -1228,7 +1230,7 @@ void QLabelPrivate::_q_buddyDeleted()
#endif // QT_NO_SHORTCUT
#if QT_CONFIG(movie)
-void QLabelPrivate::_q_movieUpdated(const QRect& rect)
+void QLabelPrivate::movieUpdated(const QRect &rect)
{
Q_Q(QLabel);
if (movie && movie->isValid()) {
@@ -1251,12 +1253,12 @@ void QLabelPrivate::_q_movieUpdated(const QRect& rect)
}
}
-void QLabelPrivate::_q_movieResized(const QSize& size)
+void QLabelPrivate::movieResized(const QSize &size)
{
Q_Q(QLabel);
q->update(); //we need to refresh the whole background in case the new size is smaller
valid_hints = false;
- _q_movieUpdated(QRect(QPoint(0,0), size));
+ movieUpdated(QRect(QPoint(0,0), size));
q->updateGeometry();
}
@@ -1278,8 +1280,10 @@ void QLabel::setMovie(QMovie *movie)
return;
d->movie = movie;
- connect(movie, SIGNAL(resized(QSize)), this, SLOT(_q_movieResized(QSize)));
- connect(movie, SIGNAL(updated(QRect)), this, SLOT(_q_movieUpdated(QRect)));
+ d->movieConnections = {
+ QObjectPrivate::connect(movie, &QMovie::resized, d, &QLabelPrivate::movieResized),
+ QObjectPrivate::connect(movie, &QMovie::updated, d, &QLabelPrivate::movieUpdated),
+ };
// Assume that if the movie is running,
// resize/update signals will come soon enough
@@ -1317,10 +1321,8 @@ void QLabelPrivate::clearContents()
shortcutId = 0;
#endif
#if QT_CONFIG(movie)
- if (movie) {
- QObject::disconnect(movie, SIGNAL(resized(QSize)), q, SLOT(_q_movieResized(QSize)));
- QObject::disconnect(movie, SIGNAL(updated(QRect)), q, SLOT(_q_movieUpdated(QRect)));
- }
+ for (const auto &conn : std::as_const(movieConnections))
+ QObject::disconnect(conn);
movie = nullptr;
#endif
#ifndef QT_NO_CURSOR
@@ -1580,12 +1582,12 @@ void QLabelPrivate::ensureTextControl() const
control->setOpenExternalLinks(openExternalLinks);
control->setPalette(q->palette());
control->setFocus(q->hasFocus());
- QObject::connect(control, SIGNAL(updateRequest(QRectF)),
- q, SLOT(update()));
- QObject::connect(control, SIGNAL(linkHovered(QString)),
- q, SLOT(_q_linkHovered(QString)));
- QObject::connect(control, SIGNAL(linkActivated(QString)),
- q, SIGNAL(linkActivated(QString)));
+ QObject::connect(control, &QWidgetTextControl::updateRequest,
+ q, qOverload<>(&QLabel::update));
+ QObject::connect(control, &QWidgetTextControl::linkActivated,
+ q, &QLabel::linkActivated);
+ QObjectPrivate::connect(control, &QWidgetTextControl::linkHovered,
+ this, &QLabelPrivate::linkHovered);
textLayoutDirty = true;
textDirty = true;
}
@@ -1601,7 +1603,7 @@ void QLabelPrivate::sendControlEvent(QEvent *e)
control->processEvent(e, -layoutRect().topLeft(), q);
}
-void QLabelPrivate::_q_linkHovered(const QString &anchor)
+void QLabelPrivate::linkHovered(const QString &anchor)
{
Q_Q(QLabel);
#ifndef QT_NO_CURSOR
@@ -1652,15 +1654,10 @@ QPoint QLabelPrivate::layoutPoint(const QPoint& p) const
#ifndef QT_NO_CONTEXTMENU
QMenu *QLabelPrivate::createStandardContextMenu(const QPoint &pos)
{
- if (!control || effectiveTextFormat == Qt::PlainText)
+ if (!control)
return nullptr;
const QPoint p = layoutPoint(pos);
- QString linkToCopy = control->document()->documentLayout()->anchorAt(p);
-
- if (linkToCopy.isEmpty())
- return nullptr;
-
return control->createStandardContextMenu(p, q_func());
}
#endif
diff --git a/src/widgets/widgets/qlabel.h b/src/widgets/widgets/qlabel.h
index ddbe05e82a..3749ec366a 100644
--- a/src/widgets/widgets/qlabel.h
+++ b/src/widgets/widgets/qlabel.h
@@ -130,15 +130,6 @@ protected:
private:
Q_DISABLE_COPY(QLabel)
Q_DECLARE_PRIVATE(QLabel)
-#if QT_CONFIG(movie)
- Q_PRIVATE_SLOT(d_func(), void _q_movieUpdated(const QRect&))
- Q_PRIVATE_SLOT(d_func(), void _q_movieResized(const QSize&))
-#endif
- Q_PRIVATE_SLOT(d_func(), void _q_linkHovered(const QString &))
-
-#ifndef QT_NO_SHORTCUT
- Q_PRIVATE_SLOT(d_func(), void _q_buddyDeleted())
-#endif
friend class QTipLabel;
friend class QMessageBoxPrivate;
friend class QBalloonTip;
diff --git a/src/widgets/widgets/qlabel_p.h b/src/widgets/widgets/qlabel_p.h
index 4718a07614..fb6ccb04eb 100644
--- a/src/widgets/widgets/qlabel_p.h
+++ b/src/widgets/widgets/qlabel_p.h
@@ -33,6 +33,9 @@
#include "qmenu.h"
#endif
+#include <QtCore/qpointer.h>
+
+#include <array>
#include <optional>
QT_BEGIN_NAMESPACE
@@ -50,12 +53,12 @@ public:
QSize sizeForWidth(int w) const;
#if QT_CONFIG(movie)
- void _q_movieUpdated(const QRect&);
- void _q_movieResized(const QSize&);
+ void movieUpdated(const QRect &rect);
+ void movieResized(const QSize &size);
#endif
#ifndef QT_NO_SHORTCUT
void updateShortcut();
- void _q_buddyDeleted();
+ void buddyDeleted();
#endif
inline bool needTextControl() const {
Q_Q(const QLabel);
@@ -70,7 +73,7 @@ public:
void ensureTextControl() const;
void sendControlEvent(QEvent *e);
- void _q_linkHovered(const QString &link);
+ void linkHovered(const QString &link);
QRectF layoutRect() const;
QRect documentRect() const;
@@ -91,6 +94,7 @@ public:
#endif
#if QT_CONFIG(movie)
QPointer<QMovie> movie;
+ std::array<QMetaObject::Connection, 2> movieConnections;
#endif
mutable QWidgetTextControl *control;
mutable QTextCursor shortcutCursor;
diff --git a/src/widgets/widgets/qlcdnumber.cpp b/src/widgets/widgets/qlcdnumber.cpp
index eef2685569..3a65b06415 100644
--- a/src/widgets/widgets/qlcdnumber.cpp
+++ b/src/widgets/widgets/qlcdnumber.cpp
@@ -69,7 +69,7 @@ public:
Incidentally, QLCDNumber is the very oldest part of Qt, tracing
its roots back to a BASIC program on the \l{Sinclair Spectrum}{Sinclair Spectrum}.
- \sa QLabel, QFrame, {Digital Clock Example}, {Tetrix Example}
+ \sa QLabel, QFrame
*/
/*!
@@ -1123,7 +1123,7 @@ void QLCDNumberPrivate::drawSegment(const QPoint &pos, char segmentNo, QPainter
/*!
\property QLCDNumber::segmentStyle
- \brief the style of the LCDNumber
+ \brief the style of the LCDNumber.
\table
\header \li Style \li Result
diff --git a/src/widgets/widgets/qlineedit.cpp b/src/widgets/widgets/qlineedit.cpp
index aad75207a1..8909ac80d9 100644
--- a/src/widgets/widgets/qlineedit.cpp
+++ b/src/widgets/widgets/qlineedit.cpp
@@ -100,55 +100,66 @@ void QLineEdit::initStyleOption(QStyleOptionFrame *option) const
\image windows-lineedit.png
- A line edit allows the user to enter and edit a single line of
- plain text with a useful collection of editing functions,
- including undo and redo, cut and paste, and drag and drop (see
- \l setDragEnabled()).
+ A line edit allows users to enter and edit a single line of
+ plain text with useful editing functions, including undo and redo, cut and
+ paste, and drag and drop.
By changing the echoMode() of a line edit, it can also be used as
- a "write-only" field, for inputs such as passwords.
+ a write-only field for inputs such as passwords.
- The length of the text can be constrained to maxLength(). The text
- can be arbitrarily constrained using a validator() or an
- inputMask(), or both. When switching between a validator and an input mask
- on the same line edit, it is best to clear the validator or input mask to
- prevent undefined behavior.
-
- A related class is QTextEdit which allows multi-line, rich text
+ QTextEdit is a related class that allows multi-line, rich text
editing.
- You can change the text with setText() or insert(). The text is
- retrieved with text(); the displayed text (which may be different,
- see \l{EchoMode}) is retrieved with displayText(). Text can be
- selected with setSelection() or selectAll(), and the selection can
- be cut(), copy()ied and paste()d. The text can be aligned with
- setAlignment().
-
- When the text changes the textChanged() signal is emitted; when
- the text changes other than by calling setText() the textEdited()
- signal is emitted; when the cursor is moved the
- cursorPositionChanged() signal is emitted; and when the Return or
- Enter key is pressed the returnPressed() signal is emitted.
-
- When editing is finished, either because the line edit lost focus
- or Return/Enter is pressed the editingFinished() signal is
- emitted. Note that if focus is lost without any changes done,
- the editingFinished() signal won't be emitted.
-
- Note that if there is a validator set on the line edit, the
- returnPressed()/editingFinished() signals will only be emitted if
- the validator returns QValidator::Acceptable.
-
- By default, QLineEdits have a frame as specified by platform
- style guides; you can turn it off by calling
- setFrame(false).
-
- The default key bindings are described below. The line edit also
- provides a context menu (usually invoked by a right mouse click)
- that presents some of these editing options.
+ \section1 Constraining Text
+
+ Use \l maxLength to define the maximum permitted length of a text. You can
+ use a \l inputMask and \l setValidator() to further constrain the text
+ content.
+
+ \section1 Editing Text
+
+ You can change the text with setText() or insert(). Use text() to retrieve
+ the text and displayText() to retrieve the displayed text (which may be
+ different, see \l{EchoMode}). You can select the text with setSelection() or
+ selectAll(), and you can cut(), copy(), and paste() the selection. To align
+ the text, use setAlignment().
+
+ When the text changes, the textChanged() signal is emitted. When the text
+ changes in some other way than by calling setText(), the textEdited() signal
+ is emitted. When the cursor is moved, the cursorPositionChanged() signal is
+ emitted. And when the Return or Enter key is selected, the returnPressed()
+ signal is emitted.
+
+ When text editing is finished, either because the line edit lost focus
+ or Return/Enter was selected, the editingFinished() signal is emitted.
+
+ If the line edit focus is lost without any text changes, the
+ editingFinished() signal won't be emitted.
+
+ If there is a validator set on the line edit, the
+ returnPressed()/editingFinished() signals will only be emitted if the
+ validator returns QValidator::Acceptable.
+
+ For more information on the many ways that QLineEdit can be used, see
+ \l {Line Edits Example}, which also provides a selection of line edit
+ examples that show the effects of various properties and validators on the
+ input and output supplied by the user.
+
+ \section1 Setting a Frame
+
+ By default, QLineEdits have a frame as specified in the platform
+ style guides. You can turn the frame off by calling setFrame(false).
+
+ \section1 Default Key Bindings
+
+ The table below describes the default key bindings.
+
+ \note The line edit also provides a context menu (usually invoked by a
+ right-click) that presents some of the editing options listed below.
+
\target desc
\table
- \header \li Keypress \li Action
+ \header \li Keystroke \li Action
\row \li Left Arrow \li Moves the cursor one character to the left.
\row \li Shift+Left Arrow \li Moves and selects text one character to the left.
\row \li Right Arrow \li Moves the cursor one character to the right.
@@ -159,7 +170,7 @@ void QLineEdit::initStyleOption(QStyleOptionFrame *option) const
\row \li Ctrl+Backspace \li Deletes the word to the left of the cursor.
\row \li Delete \li Deletes the character to the right of the cursor.
\row \li Ctrl+Delete \li Deletes the word to the right of the cursor.
- \row \li Ctrl+A \li Select all.
+ \row \li Ctrl+A \li Selects all.
\row \li Ctrl+C \li Copies the selected text to the clipboard.
\row \li Ctrl+Insert \li Copies the selected text to the clipboard.
\row \li Ctrl+K \li Deletes to the end of the line.
@@ -171,8 +182,8 @@ void QLineEdit::initStyleOption(QStyleOptionFrame *option) const
\row \li Ctrl+Y \li Redoes the last undone operation.
\endtable
- Any other key sequence that represents a valid character, will
- cause the character to be inserted into the line edit.
+ Any other keystroke that represents a valid character, will cause the
+ character to be inserted into the line edit.
\sa QTextEdit, QLabel, QComboBox, {Line Edits Example}
*/
@@ -230,13 +241,11 @@ QLineEdit::QLineEdit(QWidget* parent)
}
/*!
- Constructs a line edit containing the text \a contents.
-
- The cursor position is set to the end of the line and the maximum
- text length to 32767 characters.
+ Constructs a line edit containing the text \a contents as a child of
+ \a parent.
- The \a parent and argument is sent to the QWidget
- constructor.
+ The cursor position is set to the end of the line and the maximum text
+ length to 32767 characters.
\sa text(), setMaxLength()
*/
@@ -260,10 +269,10 @@ QLineEdit::~QLineEdit()
/*!
\property QLineEdit::text
- \brief the line edit's text.
+ \brief The line edit's text.
Setting this property clears the selection, clears the undo/redo
- history, moves the cursor to the end of the line and resets the
+ history, moves the cursor to the end of the line, and resets the
\l modified property to false. The text is not validated when
inserted with setText().
@@ -289,7 +298,7 @@ void QLineEdit::setText(const QString& text)
\since 4.7
\property QLineEdit::placeholderText
- \brief the line edit's placeholder text.
+ \brief The line edit's placeholder text.
Setting this property makes the line edit display a grayed-out
placeholder text as long as the line edit is empty.
@@ -321,12 +330,12 @@ void QLineEdit::setPlaceholderText(const QString& placeholderText)
/*!
\property QLineEdit::displayText
- \brief the displayed text.
+ \brief The displayed text.
- If \l echoMode is \l Normal this returns the same as text(); if
- \l EchoMode is \l Password or \l PasswordEchoOnEdit it returns a string of
- platform-dependent password mask characters text().length() in size,
- e.g. "******"; if \l EchoMode is \l NoEcho returns an empty string, "".
+ If \l echoMode is \l Normal, this returns the same as text(). If
+ \l EchoMode is \l Password or \l PasswordEchoOnEdit, it returns a string of
+ platform-dependent password mask characters (e.g. "******"). If \l EchoMode
+ is \l NoEcho, it returns an empty string.
By default, this property contains an empty string.
@@ -342,12 +351,12 @@ QString QLineEdit::displayText() const
/*!
\property QLineEdit::maxLength
- \brief the maximum permitted length of the text.
+ \brief The maximum permitted length of the text.
If the text is too long, it is truncated at the limit.
- If truncation occurs any selected text will be unselected, the
- cursor position is set to 0 and the first part of the string is
+ If truncation occurs, any selected text will be unselected, the
+ cursor position is set to 0, and the first part of the string is
shown.
If the line edit has an input mask, the mask defines the maximum
@@ -372,10 +381,10 @@ void QLineEdit::setMaxLength(int maxLength)
/*!
\property QLineEdit::frame
- \brief whether the line edit draws itself with a frame.
+ \brief Whether the line edit draws itself with a frame.
- If enabled (the default) the line edit draws itself inside a
- frame, otherwise the line edit draws itself without any frame.
+ If enabled (the default), the line edit draws itself inside a
+ frame. Otherwise, the line edit draws itself without any frame.
*/
bool QLineEdit::hasFrame() const
{
@@ -435,9 +444,9 @@ QAction *QLineEdit::addAction(const QIcon &icon, ActionPosition position)
\property QLineEdit::clearButtonEnabled
\brief Whether the line edit displays a clear button when it is not empty.
- If enabled, the line edit displays a trailing \e clear button when it contains
- some text, otherwise the line edit does not show a clear button (the
- default).
+ If enabled, the line edit displays a trailing \uicontrol clear button when
+ it contains some text. Otherwise, the line edit does not show a
+ \uicontrol clear button (the default).
\sa addAction(), removeAction()
\since 5.2
@@ -501,9 +510,8 @@ void QLineEdit::setFrame(bool enable)
password should be kept secret.
\value Password Display platform-dependent password mask characters instead
of the characters actually entered.
- \value PasswordEchoOnEdit Display characters as they are entered
- while editing otherwise display characters as with
- \c Password.
+ \value PasswordEchoOnEdit Display characters only while they are entered.
+ Otherwise, display characters as with \c Password.
\sa setEchoMode(), echoMode()
*/
@@ -511,13 +519,13 @@ void QLineEdit::setFrame(bool enable)
/*!
\property QLineEdit::echoMode
- \brief the line edit's echo mode.
+ \brief The line edit's echo mode.
The echo mode determines how the text entered in the line edit is
displayed (or echoed) to the user.
The most common setting is \l Normal, in which the text entered by the
- user is displayed verbatim, but QLineEdit also supports modes that allow
+ user is displayed verbatim. QLineEdit also supports modes that allow
the entered text to be suppressed or obscured: these include \l NoEcho,
\l Password and \l PasswordEchoOnEdit.
@@ -574,15 +582,15 @@ const QValidator * QLineEdit::validator() const
value during editing, but will be prevented from editing the text to a
value that \a v validates as \l{QValidator::}{Invalid}.
- This allows you to constrain the text that shall finally be entered when editing is
- done, while leaving users with enough freedom to edit the text from one valid state
- to another.
+ This allows you to constrain the text that will be stored when editing is
+ done while leaving users with enough freedom to edit the text from one valid
+ state to another.
- If \a v == 0, setValidator() removes the current input validator.
- The initial setting is to have no input validator (i.e. any input
- is accepted up to maxLength()).
+ To remove the current input validator, pass \c nullptr. The initial setting
+ is to have no input validator (any input is accepted up to maxLength()).
- \sa validator(), hasAcceptableInput(), QIntValidator, QDoubleValidator, QRegularExpressionValidator
+ \sa validator(), hasAcceptableInput(), QIntValidator, QDoubleValidator,
+ QRegularExpressionValidator
*/
void QLineEdit::setValidator(const QValidator *v)
@@ -604,8 +612,7 @@ void QLineEdit::setValidator(const QValidator *v)
use the QSortFilterProxyModel to ensure that the QCompleter's model contains
only valid entries.
- If \a c == 0, setCompleter() removes the current completer, effectively
- disabling auto completion.
+ To remove the completer and disable auto-completion, pass a \c nullptr.
\sa QCompleter
*/
@@ -615,7 +622,7 @@ void QLineEdit::setCompleter(QCompleter *c)
if (c == d->control->completer())
return;
if (d->control->completer()) {
- disconnect(d->control->completer(), nullptr, this, nullptr);
+ d->disconnectCompleter();
d->control->completer()->setWidget(nullptr);
if (d->control->completer()->parent() == this)
delete d->control->completer();
@@ -625,12 +632,8 @@ void QLineEdit::setCompleter(QCompleter *c)
return;
if (c->widget() == nullptr)
c->setWidget(this);
- if (hasFocus()) {
- QObject::connect(d->control->completer(), SIGNAL(activated(QString)),
- this, SLOT(setText(QString)));
- QObject::connect(d->control->completer(), SIGNAL(highlighted(QString)),
- this, SLOT(_q_completionHighlighted(QString)));
- }
+ if (hasFocus())
+ d->connectCompleter();
}
/*!
@@ -698,7 +701,7 @@ QSize QLineEdit::minimumSizeHint() const
/*!
\property QLineEdit::cursorPosition
- \brief the current cursor position for this line edit.
+ \brief The current cursor position for this line edit.
Setting the cursor position causes a repaint when appropriate.
@@ -731,7 +734,7 @@ int QLineEdit::cursorPositionAt(const QPoint &pos)
/*!
\property QLineEdit::alignment
- \brief the alignment of the line edit.
+ \brief The alignment of the line edit.
Both horizontal and vertical alignment is allowed here, Qt::AlignJustify
will map to Qt::AlignLeft.
@@ -756,9 +759,9 @@ void QLineEdit::setAlignment(Qt::Alignment alignment)
/*!
- Moves the cursor forward \a steps characters. If \a mark is true
- each character moved over is added to the selection; if \a mark is
- false the selection is cleared.
+ Moves the cursor forward \a steps characters. If \a mark is true,
+ each character moved over is added to the selection. If \a mark is
+ false, the selection is cleared.
\sa cursorBackward()
*/
@@ -771,9 +774,9 @@ void QLineEdit::cursorForward(bool mark, int steps)
/*!
- Moves the cursor back \a steps characters. If \a mark is true each
- character moved over is added to the selection; if \a mark is
- false the selection is cleared.
+ Moves the cursor back \a steps characters. If \a mark is true, each
+ character moved over is added to the selection. If \a mark is
+ false, the selection is cleared.
\sa cursorForward()
*/
@@ -810,9 +813,9 @@ void QLineEdit::cursorWordBackward(bool mark)
/*!
If no text is selected, deletes the character to the left of the
- text cursor and moves the cursor one position to the left. If any
+ text cursor, and moves the cursor one position to the left. If any
text is selected, the cursor is moved to the beginning of the
- selected text and the selected text is deleted.
+ selected text, and the selected text is deleted.
\sa del()
*/
@@ -825,7 +828,7 @@ void QLineEdit::backspace()
/*!
If no text is selected, deletes the character to the right of the
text cursor. If any text is selected, the cursor is moved to the
- beginning of the selected text and the selected text is deleted.
+ beginning of the selected text, and the selected text is deleted.
\sa backspace()
*/
@@ -839,7 +842,7 @@ void QLineEdit::del()
/*!
Moves the text cursor to the beginning of the line unless it is
already there. If \a mark is true, text is selected towards the
- first position; otherwise, any selected text is unselected if the
+ first position. Otherwise, any selected text is unselected if the
cursor is moved.
\sa end()
@@ -854,7 +857,7 @@ void QLineEdit::home(bool mark)
/*!
Moves the text cursor to the end of the line unless it is already
there. If \a mark is true, text is selected towards the last
- position; otherwise, any selected text is unselected if the cursor
+ position. Otherwise, any selected text is unselected if the cursor
is moved.
\sa home()
@@ -869,14 +872,14 @@ void QLineEdit::end(bool mark)
/*!
\property QLineEdit::modified
- \brief whether the line edit's contents has been modified by the user.
+ \brief Whether the line edit's contents has been modified by the user.
The modified flag is never read by QLineEdit; it has a default value
of false and is changed to true whenever the user changes the line
edit's contents.
This is useful for things that need to provide a default value but
- do not start out knowing what the default should be (perhaps it
+ do not start out knowing what the default should be (for example, it
depends on other fields on the form). Start the line edit without
the best default, and when the default is known, if modified()
returns \c false (the user hasn't entered any text), insert the
@@ -899,10 +902,10 @@ void QLineEdit::setModified(bool modified)
/*!
\property QLineEdit::hasSelectedText
- \brief whether there is any text selected.
+ \brief Whether there is any text selected.
hasSelectedText() returns \c true if some or all of the text has been
- selected by the user; otherwise returns \c false.
+ selected by the user. Otherwise, it returns \c false.
By default, this property is \c false.
@@ -918,9 +921,9 @@ bool QLineEdit::hasSelectedText() const
/*!
\property QLineEdit::selectedText
- \brief the selected text.
+ \brief The selected text.
- If there is no selected text this property's value is
+ If there is no selected text, this property's value is
an empty string.
By default, this property contains an empty string.
@@ -936,7 +939,7 @@ QString QLineEdit::selectedText() const
/*!
Returns the index of the first selected character in the
- line edit or -1 if no text is selected.
+ line edit (or -1 if no text is selected).
\sa selectedText()
\sa selectionEnd()
@@ -951,7 +954,7 @@ int QLineEdit::selectionStart() const
/*!
Returns the index of the character directly after the selection
- in the line edit or -1 if no text is selected.
+ in the line edit (or -1 if no text is selected).
\since 5.10
\sa selectedText()
@@ -1005,7 +1008,7 @@ void QLineEdit::setSelection(int start, int length)
/*!
\property QLineEdit::undoAvailable
- \brief whether undo is available.
+ \brief Whether undo is available.
Undo becomes available once the user has modified the text in the line edit.
@@ -1020,10 +1023,10 @@ bool QLineEdit::isUndoAvailable() const
/*!
\property QLineEdit::redoAvailable
- \brief whether redo is available.
+ \brief Whether redo is available.
Redo becomes available once the user has performed one or more undo operations
- on text in the line edit.
+ on the text in the line edit.
By default, this property is \c false.
*/
@@ -1036,7 +1039,7 @@ bool QLineEdit::isRedoAvailable() const
/*!
\property QLineEdit::dragEnabled
- \brief whether the lineedit starts a drag if the user presses and
+ \brief Whether the line edit starts a drag if the user presses and
moves the mouse on some selected text.
Dragging is disabled by default.
@@ -1056,17 +1059,18 @@ void QLineEdit::setDragEnabled(bool b)
/*!
\property QLineEdit::cursorMoveStyle
- \brief the movement style of cursor in this line edit.
+ \brief The movement style of the cursor in this line edit.
\since 4.8
- When this property is set to Qt::VisualMoveStyle, the line edit will use visual
- movement style. Pressing the left arrow key will always cause the cursor to move
- left, regardless of the text's writing direction. The same behavior applies to
- right arrow key.
+ When this property is set to Qt::VisualMoveStyle, the line edit will use a
+ visual movement style. Using the left arrow key will always cause the
+ cursor to move left, regardless of the text's writing direction. The same
+ behavior applies to the right arrow key.
- When the property is Qt::LogicalMoveStyle (the default), within a LTR text block,
- increase cursor position when pressing left arrow key, decrease cursor position
- when pressing the right arrow key. If the text block is right to left, the opposite
+ When the property is set to Qt::LogicalMoveStyle (the default), within a
+ left-to-right (LTR) text block, using the left arrow key will increase
+ the cursor position, whereas using the right arrow key will decrease the
+ cursor position. If the text block is right-to-left (RTL), the opposite
behavior applies.
*/
@@ -1084,7 +1088,7 @@ void QLineEdit::setCursorMoveStyle(Qt::CursorMoveStyle style)
/*!
\property QLineEdit::acceptableInput
- \brief whether the input satisfies the inputMask and the
+ \brief Whether the input satisfies the inputMask and the
validator.
By default, this property is \c true.
@@ -1098,11 +1102,11 @@ bool QLineEdit::hasAcceptableInput() const
}
/*!
+ \since 4.5
Sets the margins around the text inside the frame to have the
sizes \a left, \a top, \a right, and \a bottom.
- \since 4.5
- See also textMargins().
+ \sa textMargins()
*/
void QLineEdit::setTextMargins(int left, int top, int right, int bottom)
{
@@ -1113,7 +1117,7 @@ void QLineEdit::setTextMargins(int left, int top, int right, int bottom)
\since 4.6
Sets the \a margins around the text inside the frame.
- See also textMargins().
+ \sa textMargins()
*/
void QLineEdit::setTextMargins(const QMargins &margins)
{
@@ -1139,41 +1143,47 @@ QMargins QLineEdit::textMargins() const
\property QLineEdit::inputMask
\brief The validation input mask.
- If no mask is set, inputMask() returns an empty string.
-
Sets the QLineEdit's validation mask. Validators can be used
- instead of, or in conjunction with masks; see setValidator().
+ instead of, or in conjunction with masks; see setValidator(). The default is
+ an empty string, which means that no input mask is used.
- Unset the mask and return to normal QLineEdit operation by passing
- an empty string ("").
+ To unset the mask and return to normal QLineEdit operation, pass an empty
+ string.
- The input mask is an input template string. It can contain the following elements:
+ The input mask is an input template string. It can contain the following
+ elements:
\table
- \row \li Mask Characters \li Defines the \l {QChar::} {Category} of input characters
- that are considered valid in this position
- \row \li Meta Characters \li Various special meanings
- \row \li Separators \li All other characters are regarded as immutable separators
+ \row \li Mask Characters \li Defines the \l {QChar::} {Category} of input
+ characters that are considered valid in this position.
+ \row \li Meta Characters \li Various special meanings (see details below).
+ \row \li Separators \li All other characters are regarded as immutable
+ separators.
\endtable
- The following table shows the mask and meta characters that can be used in an input mask.
+ The following table shows the mask and meta characters that can be used in
+ an input mask.
\table
\header \li Mask Character \li Meaning
- \row \li \c A \li character of the Letter category required, such as A-Z, a-z.
- \row \li \c a \li character of the Letter category permitted but not required.
- \row \li \c N \li character of the Letter or Number category required, such as
- A-Z, a-z, 0-9.
- \row \li \c n \li character of the Letter or Number category permitted but not required.
+ \row \li \c A \li Character of the Letter category required, such as A-Z,
+ a-z.
+ \row \li \c a \li Character of the Letter category permitted but not
+ required.
+ \row \li \c N \li Character of the Letter or Number category required, such
+ as A-Z, a-z, 0-9.
+ \row \li \c n \li Character of the Letter or Number category permitted but
+ not required.
\row \li \c X \li Any non-blank character required.
\row \li \c x \li Any non-blank character permitted but not required.
- \row \li \c 9 \li character of the Number category required, e.g 0-9.
- \row \li \c 0 \li character of the Number category permitted but not required.
- \row \li \c D \li character of the Number category and larger than zero required,
- such as 1-9
- \row \li \c d \li character of the Number category and larger than zero permitted but not
- required, such as 1-9.
- \row \li \c # \li character of the Number category, or plus/minus sign permitted but not
+ \row \li \c 9 \li Character of the Number category required, such as 0-9.
+ \row \li \c 0 \li Character of the Number category permitted but not
required.
+ \row \li \c D \li Character of the Number category and larger than zero
+ required, such as 1-9.
+ \row \li \c d \li Character of the Number category and larger than zero
+ permitted but not required, such as 1-9.
+ \row \li \c # \li Character of the Number category, or plus/minus sign
+ permitted but not required.
\row \li \c H \li Hexadecimal character required. A-F, a-f, 0-9.
\row \li \c h \li Hexadecimal character permitted but not required.
\row \li \c B \li Binary character required. 0-1.
@@ -1182,11 +1192,11 @@ QMargins QLineEdit::textMargins() const
\row \li \c > \li All following alphabetic characters are uppercased.
\row \li \c < \li All following alphabetic characters are lowercased.
\row \li \c ! \li Switch off case conversion.
- \row \li \c {;c} \li Terminates the input mask and sets the \e{blank} character to \e{c}.
+ \row \li \c {;c} \li Terminates the input mask and sets the \e{blank}
+ character to \e{c}.
\row \li \c {[ ] { }} \li Reserved.
- \row \li \tt{\\} \li Use \tt{\\} to escape the special
- characters listed above to use them as
- separators.
+ \row \li \tt{\\} \li Use \tt{\\} to escape the special characters listed
+ above to use them as separators.
\endtable
When created or cleared, the line edit will be filled with a copy of the
@@ -1199,7 +1209,7 @@ QMargins QLineEdit::textMargins() const
unmodified content can be read using displayText().
The hasAcceptableInput() method returns false if the current content of the
- line edit does not fulfil the requirements of the input mask.
+ line edit does not fulfill the requirements of the input mask.
Examples:
\table
@@ -1230,8 +1240,10 @@ void QLineEdit::setInputMask(const QString &inputMask)
}
/*!
- Selects all the text (i.e. highlights it) and moves the cursor to
- the end. This is useful when a default value has been inserted
+ Selects all the text (highlights it) and moves the cursor to
+ the end.
+
+ \note This is useful when a default value has been inserted
because if the user types before clicking on the widget, the
selected text will be deleted.
@@ -1259,7 +1271,7 @@ void QLineEdit::deselect()
/*!
Deletes any selected text, inserts \a newText, and validates the
- result. If it is valid, it sets it as the new contents of the line
+ result. If it is valid, it sets the new text as the new contents of the line
edit.
\sa setText(), clear()
@@ -1308,7 +1320,7 @@ void QLineEdit::redo()
/*!
\property QLineEdit::readOnly
- \brief whether the line edit is read only.
+ \brief Whether the line edit is read-only.
In read-only mode, the user can still copy the text to the
clipboard, or drag and drop the text (if echoMode() is \l Normal),
@@ -1431,7 +1443,10 @@ bool QLineEdit::event(QEvent * e)
#endif
//d->separate();
} else if (e->type() == QEvent::WindowActivate) {
- QTimer::singleShot(0, this, SLOT(_q_handleWindowActivate()));
+ QTimer::singleShot(0, this, [this]() {
+ Q_D(QLineEdit);
+ d->handleWindowActivate();
+ });
#ifndef QT_NO_SHORTCUT
} else if (e->type() == QEvent::ShortcutOverride) {
QKeyEvent *ke = static_cast<QKeyEvent*>(e);
@@ -1447,6 +1462,8 @@ bool QLineEdit::event(QEvent * e)
|| style()->styleHint(QStyle::SH_BlinkCursorWhenTextSelected, &opt, this))
d->setCursorVisible(true);
}
+ } else if (e->type() == QEvent::Hide) {
+ d->control->setBlinkingCursorEnabled(false);
#if QT_CONFIG(action)
} else if (e->type() == QEvent::ActionRemoved) {
d->removeAction(static_cast<QActionEvent *>(e)->action());
@@ -1647,8 +1664,9 @@ void QLineEdit::mouseDoubleClickEvent(QMouseEvent* e)
/*!
\fn void QLineEdit::returnPressed()
- This signal is emitted when the Return or Enter key is pressed.
- Note that if there is a validator() or inputMask() set on the line
+ This signal is emitted when the Return or Enter key is used.
+
+ \note If there is a validator() or inputMask() set on the line
edit, the returnPressed() signal will only be emitted if the input
follows the inputMask() and the validator() returns
QValidator::Acceptable.
@@ -1657,41 +1675,39 @@ void QLineEdit::mouseDoubleClickEvent(QMouseEvent* e)
/*!
\fn void QLineEdit::editingFinished()
- This signal is emitted when the Return or Enter key is pressed, or
- if the line edit loses focus and its contents have changed since the
- last time this signal was emitted.
+ This signal is emitted when the Return or Enter key is used, or if the line
+ edit loses focus and its contents have changed since the last time this
+ signal was emitted.
- Note that if there is a validator() or
- inputMask() set on the line edit and enter/return is pressed, the
- editingFinished() signal will only be emitted if the input follows
- the inputMask() and the validator() returns QValidator::Acceptable.
+ \note If there is a validator() or inputMask() set on the line edit and
+ enter/return is used, the editingFinished() signal will only be emitted
+ if the input follows the inputMask() and the validator() returns
+ QValidator::Acceptable.
*/
/*!
\fn void QLineEdit::inputRejected()
\since 5.12
- This signal is emitted when the user presses a key that is not
- considered to be acceptable input. For example, if a key press
- results in a validator's validate() call to return Invalid.
- Another case is when trying to enter in more characters beyond the
- maximum length of the line edit.
+ This signal is emitted when the user uses a key that is not
+ considered to be valid input. For example, if using a key results in a
+ validator's \l {QValidator::validate()}{validate()} call to return
+ \l {QValidator::Invalid}{Invalid}. Another case is when trying
+ to enter more characters beyond the maximum length of the line edit.
- Note: This signal will still be emitted in a case where part of
- the text is accepted but not all of it is. For example, if there
- is a maximum length set and the clipboard text is longer than the
- maximum length when it is pasted.
+ \note This signal will still be emitted when only a part of the text is
+ accepted. For example, if there is a maximum length set and the clipboard
+ text is longer than the maximum length when it is pasted.
*/
/*!
Converts the given key press \a event into a line edit action.
- If Return or Enter is pressed and the current text is valid (or
+ If Return or Enter is used and the current text is valid (or
can be \l{QValidator::fixup()}{made valid} by the
validator), the signal returnPressed() is emitted.
- The default key bindings are listed in the class's detailed
- description.
+ \sa {Default Key Bindings}
*/
void QLineEdit::keyPressEvent(QKeyEvent *event)
@@ -1747,18 +1763,19 @@ void QLineEdit::keyPressEvent(QKeyEvent *event)
/*!
\reimp
*/
-void QLineEdit::keyReleaseEvent(QKeyEvent *)
+void QLineEdit::keyReleaseEvent(QKeyEvent *e)
{
Q_D(QLineEdit);
if (!isReadOnly())
d->handleSoftwareInputPanel();
d->control->updateCursorBlinking();
+ QWidget::keyReleaseEvent(e);
}
/*!
\since 4.4
- Returns a rectangle that includes the lineedit cursor.
+ Returns a rectangle that includes the line edit cursor.
*/
QRect QLineEdit::cursorRect() const
{
@@ -1828,7 +1845,7 @@ QVariant QLineEdit::inputMethodQuery(Qt::InputMethodQuery property, QVariant arg
Q_D(const QLineEdit);
switch(property) {
case Qt::ImEnabled:
- return isEnabled();
+ return isEnabled() && !isReadOnly();
case Qt::ImCursorRectangle:
return d->cursorRect();
case Qt::ImAnchorRectangle:
@@ -1908,10 +1925,7 @@ void QLineEdit::focusInEvent(QFocusEvent *e)
#if QT_CONFIG(completer)
if (d->control->completer()) {
d->control->completer()->setWidget(this);
- QObject::connect(d->control->completer(), SIGNAL(activated(QString)),
- this, SLOT(setText(QString)));
- QObject::connect(d->control->completer(), SIGNAL(highlighted(QString)),
- this, SLOT(_q_completionHighlighted(QString)));
+ d->connectCompleter();
}
#endif
update();
@@ -1950,9 +1964,8 @@ void QLineEdit::focusOutEvent(QFocusEvent *e)
d->control->setCancelText(QString());
#endif
#if QT_CONFIG(completer)
- if (d->control->completer()) {
- QObject::disconnect(d->control->completer(), nullptr, this, nullptr);
- }
+ if (d->control->completer())
+ d->disconnectCompleter();
#endif
QWidget::focusOutEvent(e);
}
@@ -2083,7 +2096,7 @@ void QLineEdit::paintEvent(QPaintEvent *)
if (d->cursorVisible && !d->control->isReadOnly() && d->control->inputMask().isEmpty())
flags |= QWidgetLineControl::DrawCursor;
- d->control->setCursorWidth(style()->pixelMetric(QStyle::PM_TextCursorWidth, &panel));
+ d->control->setCursorWidth(style()->pixelMetric(QStyle::PM_TextCursorWidth, &panel, this));
d->control->draw(&p, topLeft, r, flags);
}
@@ -2162,10 +2175,10 @@ void QLineEdit::dropEvent(QDropEvent* e)
createStandardContextMenu().
If you do not want the line edit to have a context menu, you can set
- its \l contextMenuPolicy to Qt::NoContextMenu. If you want to
- customize the context menu, reimplement this function. If you want
- to extend the standard context menu, reimplement this function, call
- createStandardContextMenu() and extend the menu returned.
+ its \l contextMenuPolicy to Qt::NoContextMenu. To customize the context
+ menu, reimplement this function. To extend the standard context menu,
+ reimplement this function, call createStandardContextMenu(), and extend the
+ menu returned.
\snippet code/src_gui_widgets_qlineedit.cpp 0
@@ -2182,14 +2195,7 @@ void QLineEdit::contextMenuEvent(QContextMenuEvent *event)
}
}
-static inline void setActionIcon(QAction *action, const QString &name)
-{
- const QIcon icon = QIcon::fromTheme(name);
- if (!icon.isNull())
- action->setIcon(icon);
-}
-
-/*! This function creates the standard context menu which is shown
+/*! Creates the standard context menu, which is shown
when the user clicks on the line edit with the right mouse
button. It is called from the default contextMenuEvent() handler.
The popup menu's ownership is transferred to the caller.
@@ -2206,12 +2212,12 @@ QMenu *QLineEdit::createStandardContextMenu()
action = popup->addAction(QLineEdit::tr("&Undo") + ACCEL_KEY(QKeySequence::Undo));
action->setEnabled(d->control->isUndoAvailable());
setActionIcon(action, QStringLiteral("edit-undo"));
- connect(action, SIGNAL(triggered()), SLOT(undo()));
+ connect(action, &QAction::triggered, this, &QLineEdit::undo);
action = popup->addAction(QLineEdit::tr("&Redo") + ACCEL_KEY(QKeySequence::Redo));
action->setEnabled(d->control->isRedoAvailable());
setActionIcon(action, QStringLiteral("edit-redo"));
- connect(action, SIGNAL(triggered()), SLOT(redo()));
+ connect(action, &QAction::triggered, this, &QLineEdit::redo);
popup->addSeparator();
}
@@ -2222,20 +2228,20 @@ QMenu *QLineEdit::createStandardContextMenu()
action->setEnabled(!d->control->isReadOnly() && d->control->hasSelectedText()
&& d->control->echoMode() == QLineEdit::Normal);
setActionIcon(action, QStringLiteral("edit-cut"));
- connect(action, SIGNAL(triggered()), SLOT(cut()));
+ connect(action, &QAction::triggered, this, &QLineEdit::cut);
}
action = popup->addAction(QLineEdit::tr("&Copy") + ACCEL_KEY(QKeySequence::Copy));
action->setEnabled(d->control->hasSelectedText()
&& d->control->echoMode() == QLineEdit::Normal);
setActionIcon(action, QStringLiteral("edit-copy"));
- connect(action, SIGNAL(triggered()), SLOT(copy()));
+ connect(action, &QAction::triggered, this, &QLineEdit::copy);
if (!isReadOnly()) {
action = popup->addAction(QLineEdit::tr("&Paste") + ACCEL_KEY(QKeySequence::Paste));
action->setEnabled(!d->control->isReadOnly() && !QGuiApplication::clipboard()->text().isEmpty());
setActionIcon(action, QStringLiteral("edit-paste"));
- connect(action, SIGNAL(triggered()), SLOT(paste()));
+ connect(action, &QAction::triggered, this, &QLineEdit::paste);
}
#endif
@@ -2243,7 +2249,8 @@ QMenu *QLineEdit::createStandardContextMenu()
action = popup->addAction(QLineEdit::tr("Delete"));
action->setEnabled(!d->control->isReadOnly() && !d->control->text().isEmpty() && d->control->hasSelectedText());
setActionIcon(action, QStringLiteral("edit-delete"));
- connect(action, SIGNAL(triggered()), d->control, SLOT(_q_deleteSelected()));
+ connect(action, &QAction::triggered,
+ d->control, &QWidgetLineControl::_q_deleteSelected);
}
if (!popup->isEmpty())
@@ -2253,7 +2260,7 @@ QMenu *QLineEdit::createStandardContextMenu()
action->setEnabled(!d->control->text().isEmpty() && !d->control->allSelected());
setActionIcon(action, QStringLiteral("edit-select-all"));
d->selectAllAction = action;
- connect(action, SIGNAL(triggered()), SLOT(selectAll()));
+ connect(action, &QAction::triggered, this, &QLineEdit::selectAll);
if (!d->control->isReadOnly() && QGuiApplication::styleHints()->useRtlExtensions()) {
popup->addSeparator();
diff --git a/src/widgets/widgets/qlineedit.h b/src/widgets/widgets/qlineedit.h
index e0729f0929..8955a67895 100644
--- a/src/widgets/widgets/qlineedit.h
+++ b/src/widgets/widgets/qlineedit.h
@@ -214,20 +214,6 @@ private:
#endif
Q_DISABLE_COPY(QLineEdit)
Q_DECLARE_PRIVATE(QLineEdit)
- Q_PRIVATE_SLOT(d_func(), void _q_handleWindowActivate())
- Q_PRIVATE_SLOT(d_func(), void _q_textEdited(const QString &))
- Q_PRIVATE_SLOT(d_func(), void _q_cursorPositionChanged(int, int))
-#if QT_CONFIG(completer)
- Q_PRIVATE_SLOT(d_func(), void _q_completionHighlighted(const QString &))
-#endif
-#ifdef QT_KEYPAD_NAVIGATION
- Q_PRIVATE_SLOT(d_func(), void _q_editFocusChange(bool))
-#endif
- Q_PRIVATE_SLOT(d_func(), void _q_selectionChanged())
- Q_PRIVATE_SLOT(d_func(), void _q_updateNeeded(const QRect &))
- Q_PRIVATE_SLOT(d_func(), void _q_textChanged(const QString &))
- Q_PRIVATE_SLOT(d_func(), void _q_clearButtonClicked())
- Q_PRIVATE_SLOT(d_func(), void _q_controlEditingFinished())
};
QT_END_NAMESPACE
diff --git a/src/widgets/widgets/qlineedit_p.cpp b/src/widgets/widgets/qlineedit_p.cpp
index e846600149..fdd6a3aa4f 100644
--- a/src/widgets/widgets/qlineedit_p.cpp
+++ b/src/widgets/widgets/qlineedit_p.cpp
@@ -74,8 +74,25 @@ QRect QLineEditPrivate::cursorRect() const
}
#if QT_CONFIG(completer)
+void QLineEditPrivate::connectCompleter()
+{
+ Q_Q(const QLineEdit);
+ QObject::connect(control->completer(), qOverload<const QString &>(&QCompleter::activated),
+ q, &QLineEdit::setText);
+ QObjectPrivate::connect(control->completer(), qOverload<const QString &>(&QCompleter::highlighted),
+ this, &QLineEditPrivate::completionHighlighted);
+}
-void QLineEditPrivate::_q_completionHighlighted(const QString &newText)
+void QLineEditPrivate::disconnectCompleter()
+{
+ Q_Q(const QLineEdit);
+ QObject::disconnect(control->completer(), qOverload<const QString &>(&QCompleter::activated),
+ q, &QLineEdit::setText);
+ QObjectPrivate::disconnect(control->completer(), qOverload<const QString &>(&QCompleter::highlighted),
+ this, &QLineEditPrivate::completionHighlighted);
+}
+
+void QLineEditPrivate::completionHighlighted(const QString &newText)
{
Q_Q(QLineEdit);
if (control->completer()->completionMode() != QCompleter::InlineCompletion) {
@@ -96,14 +113,14 @@ void QLineEditPrivate::_q_completionHighlighted(const QString &newText)
#endif // QT_CONFIG(completer)
-void QLineEditPrivate::_q_handleWindowActivate()
+void QLineEditPrivate::handleWindowActivate()
{
Q_Q(QLineEdit);
if (!q->hasFocus() && control->hasSelectedText())
control->deselect();
}
-void QLineEditPrivate::_q_textEdited(const QString &text)
+void QLineEditPrivate::textEdited(const QString &text)
{
Q_Q(QLineEdit);
edited = true;
@@ -115,7 +132,7 @@ void QLineEditPrivate::_q_textEdited(const QString &text)
#endif
}
-void QLineEditPrivate::_q_cursorPositionChanged(int from, int to)
+void QLineEditPrivate::cursorPositionChanged(int from, int to)
{
Q_Q(QLineEdit);
q->update();
@@ -123,14 +140,14 @@ void QLineEditPrivate::_q_cursorPositionChanged(int from, int to)
}
#ifdef QT_KEYPAD_NAVIGATION
-void QLineEditPrivate::_q_editFocusChange(bool e)
+void QLineEditPrivate::editFocusChange(bool e)
{
Q_Q(QLineEdit);
q->setEditFocus(e);
}
#endif
-void QLineEditPrivate::_q_selectionChanged()
+void QLineEditPrivate::selectionChanged()
{
Q_Q(QLineEdit);
if (control->preeditAreaText().isEmpty()) {
@@ -150,7 +167,7 @@ void QLineEditPrivate::_q_selectionChanged()
#endif
}
-void QLineEditPrivate::_q_updateNeeded(const QRect &rect)
+void QLineEditPrivate::updateNeeded(const QRect &rect)
{
q_func()->update(adjustedControlRect(rect));
}
@@ -158,45 +175,51 @@ void QLineEditPrivate::_q_updateNeeded(const QRect &rect)
void QLineEditPrivate::init(const QString& txt)
{
Q_Q(QLineEdit);
+
+ const auto qUpdateMicroFocus = [q]()
+ {
+ q->updateMicroFocus();
+ };
control = new QWidgetLineControl(txt);
control->setParent(q);
control->setFont(q->font());
- QObject::connect(control, SIGNAL(textChanged(QString)),
- q, SIGNAL(textChanged(QString)));
- QObject::connect(control, SIGNAL(textEdited(QString)),
- q, SLOT(_q_textEdited(QString)));
- QObject::connect(control, SIGNAL(cursorPositionChanged(int,int)),
- q, SLOT(_q_cursorPositionChanged(int,int)));
- QObject::connect(control, SIGNAL(selectionChanged()),
- q, SLOT(_q_selectionChanged()));
- QObject::connect(control, SIGNAL(editingFinished()),
- q, SLOT(_q_controlEditingFinished()));
+ QObject::connect(control, &QWidgetLineControl::textChanged,
+ q, &QLineEdit::textChanged);
+ QObjectPrivate::connect(control, &QWidgetLineControl::textEdited,
+ this, &QLineEditPrivate::textEdited);
+ QObjectPrivate::connect(control, &QWidgetLineControl::cursorPositionChanged,
+ this, &QLineEditPrivate::cursorPositionChanged);
+ QObjectPrivate::connect(control, &QWidgetLineControl::selectionChanged,
+ this, &QLineEditPrivate::selectionChanged);
+ QObjectPrivate::connect(control, &QWidgetLineControl::editingFinished,
+ this, &QLineEditPrivate::controlEditingFinished);
#ifdef QT_KEYPAD_NAVIGATION
- QObject::connect(control, SIGNAL(editFocusChange(bool)),
- q, SLOT(_q_editFocusChange(bool)));
+ QObject::connect(control, &QWidgetLineControl::editFocusChange,
+ this, &QLineEditPrivate::editFocusChange);
#endif
- QObject::connect(control, SIGNAL(cursorPositionChanged(int,int)),
- q, SLOT(updateMicroFocus()));
+ QObject::connect(control, &QWidgetLineControl::cursorPositionChanged,
+ q, qUpdateMicroFocus);
- QObject::connect(control, SIGNAL(textChanged(QString)),
- q, SLOT(updateMicroFocus()));
+ QObject::connect(control, &QWidgetLineControl::textChanged,
+ q, qUpdateMicroFocus);
- QObject::connect(control, SIGNAL(updateMicroFocus()),
- q, SLOT(updateMicroFocus()));
+ QObject::connect(control, &QWidgetLineControl::updateMicroFocus,
+ q, qUpdateMicroFocus);
// for now, going completely overboard with updates.
- QObject::connect(control, SIGNAL(selectionChanged()),
- q, SLOT(update()));
+ QObject::connect(control, &QWidgetLineControl::selectionChanged,
+ q, qOverload<>(&QLineEdit::update));
- QObject::connect(control, SIGNAL(selectionChanged()),
- q, SLOT(updateMicroFocus()));
+ QObject::connect(control, &QWidgetLineControl::selectionChanged,
+ q, qUpdateMicroFocus);
- QObject::connect(control, SIGNAL(displayTextChanged(QString)),
- q, SLOT(update()));
+ QObject::connect(control, &QWidgetLineControl::displayTextChanged,
+ q, qOverload<>(&QLineEdit::update));
- QObject::connect(control, SIGNAL(updateNeeded(QRect)),
- q, SLOT(_q_updateNeeded(QRect)));
- QObject::connect(control, SIGNAL(inputRejected()), q, SIGNAL(inputRejected()));
+ QObjectPrivate::connect(control, &QWidgetLineControl::updateNeeded,
+ this, &QLineEditPrivate::updateNeeded);
+ QObject::connect(control, &QWidgetLineControl::inputRejected,
+ q, &QLineEdit::inputRejected);
QStyleOptionFrame opt;
q->initStyleOption(&opt);
@@ -409,7 +432,7 @@ void QLineEditIconButton::animateShow(bool visible)
void QLineEditIconButton::startOpacityAnimation(qreal endValue)
{
- QPropertyAnimation *animation = new QPropertyAnimation(this, QByteArrayLiteral("opacity"));
+ QPropertyAnimation *animation = new QPropertyAnimation(this, QByteArrayLiteral("opacity"), this);
connect(animation, &QPropertyAnimation::finished, this, &QLineEditIconButton::onAnimationFinished);
animation->setDuration(160);
@@ -436,7 +459,7 @@ static void displayWidgets(const QLineEditPrivate::SideWidgetEntryList &widgets,
}
#endif
-void QLineEditPrivate::_q_textChanged(const QString &text)
+void QLineEditPrivate::textChanged(const QString &text)
{
if (hasSideWidgets()) {
const int newTextSize = text.size();
@@ -451,16 +474,16 @@ void QLineEditPrivate::_q_textChanged(const QString &text)
}
}
-void QLineEditPrivate::_q_clearButtonClicked()
+void QLineEditPrivate::clearButtonClicked()
{
Q_Q(QLineEdit);
if (!q->text().isEmpty()) {
q->clear();
- _q_textEdited(QString());
+ textEdited(QString());
}
}
-void QLineEditPrivate::_q_controlEditingFinished()
+void QLineEditPrivate::controlEditingFinished()
{
Q_Q(QLineEdit);
edited = false;
@@ -554,7 +577,8 @@ QWidget *QLineEditPrivate::addAction(QAction *newAction, QAction *before, QLineE
if (!newAction)
return nullptr;
if (!hasSideWidgets()) { // initial setup.
- QObject::connect(q, SIGNAL(textChanged(QString)), q, SLOT(_q_textChanged(QString)));
+ QObjectPrivate::connect(q, &QLineEdit::textChanged,
+ this, &QLineEditPrivate::textChanged);
lastTextSize = q->text().size();
}
QWidget *w = nullptr;
@@ -570,7 +594,8 @@ QWidget *QLineEditPrivate::addAction(QAction *newAction, QAction *before, QLineE
toolButton->setIcon(newAction->icon());
toolButton->setOpacity(lastTextSize > 0 || !(flags & SideWidgetFadeInWithText) ? 1 : 0);
if (flags & SideWidgetClearButton) {
- QObject::connect(toolButton, SIGNAL(clicked()), q, SLOT(_q_clearButtonClicked()));
+ QObjectPrivate::connect(toolButton, &QToolButton::clicked,
+ this, &QLineEditPrivate::clearButtonClicked);
#if QT_CONFIG(animation)
// The clear button is handled only by this widget. The button should be really
@@ -633,7 +658,8 @@ void QLineEditPrivate::removeAction(QAction *action)
delete entry.widget;
positionSideWidgets();
if (!hasSideWidgets()) // Last widget, remove connection
- QObject::disconnect(q, SIGNAL(textChanged(QString)), q, SLOT(_q_textChanged(QString)));
+ QObjectPrivate::connect(q, &QLineEdit::textChanged,
+ this, &QLineEditPrivate::textChanged);
q->update();
}
#endif // QT_CONFIG(action)
@@ -646,7 +672,7 @@ static int effectiveTextMargin(int defaultMargin, const QLineEditPrivate::SideWi
const auto visibleSideWidgetCount = std::count_if(widgets.begin(), widgets.end(),
[](const QLineEditPrivate::SideWidgetEntry &e) {
-#if QT_CONFIG(animation)
+#if QT_CONFIG(toolbutton) && QT_CONFIG(animation)
// a button that's fading out doesn't get any space
if (auto* iconButton = qobject_cast<QLineEditIconButton*>(e.widget))
return iconButton->needsSpace();
diff --git a/src/widgets/widgets/qlineedit_p.h b/src/widgets/widgets/qlineedit_p.h
index 64350ae39a..3737ac8fe6 100644
--- a/src/widgets/widgets/qlineedit_p.h
+++ b/src/widgets/widgets/qlineedit_p.h
@@ -190,25 +190,27 @@ public:
QRect adjustedContentsRect() const;
- void _q_handleWindowActivate();
- void _q_textEdited(const QString &);
- void _q_cursorPositionChanged(int, int);
+ void handleWindowActivate();
+ void textEdited(const QString &);
+ void cursorPositionChanged(int, int);
#ifdef QT_KEYPAD_NAVIGATION
- void _q_editFocusChange(bool);
+ void editFocusChange(bool);
#endif
- void _q_selectionChanged();
- void _q_updateNeeded(const QRect &);
+ void selectionChanged();
+ void updateNeeded(const QRect &);
#if QT_CONFIG(completer)
- void _q_completionHighlighted(const QString &);
+ void connectCompleter();
+ void disconnectCompleter();
+ void completionHighlighted(const QString &);
#endif
QPoint mousePressPos;
#if QT_CONFIG(draganddrop)
QBasicTimer dndTimer;
void drag();
#endif
- void _q_textChanged(const QString &);
- void _q_clearButtonClicked();
- void _q_controlEditingFinished();
+ void textChanged(const QString &);
+ void clearButtonClicked();
+ void controlEditingFinished();
QMargins textMargins; // use effectiveTextMargins() in case of icon.
diff --git a/src/widgets/widgets/qmainwindow.cpp b/src/widgets/widgets/qmainwindow.cpp
index 256e97396b..4534058d45 100644
--- a/src/widgets/widgets/qmainwindow.cpp
+++ b/src/widgets/widgets/qmainwindow.cpp
@@ -25,6 +25,7 @@
#include <qstyle.h>
#include <qdebug.h>
#include <qpainter.h>
+#include <qmimedata.h>
#include <private/qwidget_p.h>
#if QT_CONFIG(toolbar)
@@ -36,6 +37,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
class QMainWindowPrivate : public QWidgetPrivate
{
Q_DECLARE_PUBLIC(QMainWindow)
@@ -46,7 +49,7 @@ public:
, useUnifiedToolBar(false)
#endif
{ }
- QMainWindowLayout *layout;
+ QPointer<QMainWindowLayout> layout;
QSize iconSize;
bool explicitIconSize;
Qt::ToolButtonStyle toolButtonStyle;
@@ -57,7 +60,7 @@ public:
static inline QMainWindowLayout *mainWindowLayout(const QMainWindow *mainWindow)
{
- return mainWindow ? mainWindow->d_func()->layout : static_cast<QMainWindowLayout *>(nullptr);
+ return mainWindow ? mainWindow->d_func()->layout.data() : static_cast<QMainWindowLayout *>(nullptr);
}
};
@@ -121,6 +124,7 @@ void QMainWindowPrivate::init()
const int metric = q->style()->pixelMetric(QStyle::PM_ToolBarIconSize, nullptr, q);
iconSize = QSize(metric, metric);
q->setAttribute(Qt::WA_Hover);
+ q->setAcceptDrops(true);
}
/*
@@ -276,9 +280,7 @@ void QMainWindowPrivate::init()
is the position and size (relative to the size of the main window)
of the toolbars and dock widgets that are stored.
- \sa QMenuBar, QToolBar, QStatusBar, QDockWidget,
- {Qt Widgets - Application Example}, {Dock Widgets Example},
- {MDI Example}, {SDI Example}, {Menus Example}
+ \sa QMenuBar, QToolBar, QStatusBar, QDockWidget, {Menus Example}
*/
/*!
@@ -1121,21 +1123,8 @@ void QMainWindow::tabifyDockWidget(QDockWidget *first, QDockWidget *second)
QList<QDockWidget*> QMainWindow::tabifiedDockWidgets(QDockWidget *dockwidget) const
{
- QList<QDockWidget*> ret;
- const QDockAreaLayoutInfo *info = d_func()->layout->layoutState.dockAreaLayout.info(dockwidget);
- if (info && info->tabbed && info->tabBar) {
- for(int i = 0; i < info->item_list.size(); ++i) {
- const QDockAreaLayoutItem &item = info->item_list.at(i);
- if (item.widgetItem) {
- if (QDockWidget *dock = qobject_cast<QDockWidget*>(item.widgetItem->widget())) {
- if (dock != dockwidget) {
- ret += dock;
- }
- }
- }
- }
- }
- return ret;
+ Q_D(const QMainWindow);
+ return d->layout ? d->layout->tabifiedDockWidgets(dockwidget) : QList<QDockWidget *>();
}
#endif // QT_CONFIG(tabbar)
@@ -1296,6 +1285,28 @@ bool QMainWindow::event(QEvent *event)
if (!d->explicitIconSize)
setIconSize(QSize());
break;
+#if QT_CONFIG(draganddrop)
+ case QEvent::DragEnter:
+ case QEvent::Drop:
+ if (!d->layout->draggingWidget)
+ break;
+ event->accept();
+ return true;
+ case QEvent::DragMove: {
+ if (!d->layout->draggingWidget)
+ break;
+ auto dragMoveEvent = static_cast<QDragMoveEvent *>(event);
+ d->layout->hover(d->layout->draggingWidget,
+ mapToGlobal(dragMoveEvent->position()).toPoint());
+ event->accept();
+ return true;
+ }
+ case QEvent::DragLeave:
+ if (!d->layout->draggingWidget)
+ break;
+ d->layout->hover(d->layout->draggingWidget, pos() - QPoint(-1, -1));
+ return true;
+#endif
default:
break;
}
diff --git a/src/widgets/widgets/qmainwindow.h b/src/widgets/widgets/qmainwindow.h
index 90e37d7283..a050093417 100644
--- a/src/widgets/widgets/qmainwindow.h
+++ b/src/widgets/widgets/qmainwindow.h
@@ -178,6 +178,7 @@ protected:
private:
Q_DECLARE_PRIVATE(QMainWindow)
Q_DISABLE_COPY(QMainWindow)
+ friend class QDockWidgetGroupWindow;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QMainWindow::DockOptions)
diff --git a/src/widgets/widgets/qmainwindowlayout.cpp b/src/widgets/widgets/qmainwindowlayout.cpp
index a1b1b20869..85c58fd70f 100644
--- a/src/widgets/widgets/qmainwindowlayout.cpp
+++ b/src/widgets/widgets/qmainwindowlayout.cpp
@@ -24,6 +24,10 @@
#endif
#include <qapplication.h>
+#if QT_CONFIG(draganddrop)
+#include <qdrag.h>
+#endif
+#include <qmimedata.h>
#if QT_CONFIG(statusbar)
#include <qstatusbar.h>
#endif
@@ -46,6 +50,8 @@
#include <private/qlayoutengine_p.h>
#include <private/qwidgetresizehandler_p.h>
+#include <QScopedValueRollback>
+
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
@@ -145,18 +151,57 @@ QDebug operator<<(QDebug debug, const QMainWindowLayout *layout)
return debug;
}
+// Use this to dump item lists of all populated main window docks.
+// Use DUMP macro inside QMainWindowLayout
+#if 0
+static void dumpItemLists(const QMainWindowLayout *layout, const char *function, const char *comment)
+{
+ for (int i = 0; i < QInternal::DockCount; ++i) {
+ const auto &list = layout->layoutState.dockAreaLayout.docks[i].item_list;
+ if (list.isEmpty())
+ continue;
+ qDebug() << function << comment << "Dock" << i << list;
+ }
+}
+#define DUMP(comment) dumpItemLists(this, __FUNCTION__, comment)
+#endif // 0
+
#endif // QT_CONFIG(dockwidget) && !defined(QT_NO_DEBUG)
-/******************************************************************************
- ** QDockWidgetGroupWindow
- */
-// QDockWidgetGroupWindow is the floating window containing several QDockWidgets floating together.
-// (QMainWindow::GroupedDragging feature)
-// QDockWidgetGroupLayout is the layout of that window and use a QDockAreaLayoutInfo to layout
-// the QDockWidgets inside it.
-// If there is only one QDockWidgets, or all QDockWidgets are tabbed together, it is equivalent
-// of a floating QDockWidget (the title of the QDockWidget is the title of the window). But if there
-// are nested QDockWidget, an additional title bar is there.
+
+/*!
+ \internal
+ QDockWidgetGroupWindow is a floating window, containing several QDockWidgets floating together.
+ This requires QMainWindow::GroupedDragging to be enabled.
+ QDockWidgets floating jointly in a QDockWidgetGroupWindow are considered to be docked.
+ Their \c isFloating property is \c false.
+ QDockWidget children of a QDockWidgetGroupWindow are either:
+ \list
+ \li tabbed (as long as Qt is compiled with the \c tabbar feature), or
+ \li arranged next to each other, equivalent to the default on a main window dock.
+ \endlist
+
+ QDockWidgetGroupWindow uses QDockWidgetGroupLayout to lay out its QDockWidget children.
+ It stores layout information in a QDockAreaLayoutInfo, including temporary spacer items
+ and rubber bands.
+
+ If its QDockWidget children are tabbed, the QDockWidgetGroupWindow shows the active QDockWidget's
+ title as its own window title.
+
+ QDockWidgetGroupWindow is designed to hold more than one QDockWidget.
+ A QDockWidgetGroupWindow with only one QDockWidget child may occur only temporarily
+ \list
+ \li in its construction phase, or
+ \li during a hover: While QDockWidget A is hovered over B, B is converted into a QDockWidgetGroupWindow.
+ \endlist
+
+ A QDockWidgetGroupWindow with only one QDockWidget child must never get focus, be dragged or dropped.
+ To enforce this restriction, QDockWidgetGrouWindow will remove itself after its second QDockWidget
+ child has been removed. It will make its last QDockWidget child a single, floating QDockWidget.
+ Eventually, the empty QDockWidgetGroupWindow will call deleteLater() on itself.
+*/
+
+
#if QT_CONFIG(dockwidget)
class QDockWidgetGroupLayout : public QLayout,
public QMainWindowLayoutSeparatorHelper<QDockWidgetGroupLayout>
@@ -293,6 +338,7 @@ bool QDockWidgetGroupWindow::event(QEvent *e)
case QEvent::Resize:
updateCurrentGapRect();
emit resized();
+ break;
default:
break;
}
@@ -397,8 +443,8 @@ void QDockWidgetGroupWindow::destroyOrHideIfEmpty()
}
// Make sure to reparent the possibly floating or hidden QDockWidgets to the parent
- const auto dockWidgets = findChildren<QDockWidget *>(Qt::FindDirectChildrenOnly);
- for (QDockWidget *dw : dockWidgets) {
+ const auto dockWidgetsList = dockWidgets();
+ for (QDockWidget *dw : dockWidgetsList) {
const bool wasFloating = dw->isFloating();
const bool wasHidden = dw->isHidden();
dw->setParent(parentWidget());
@@ -418,14 +464,28 @@ void QDockWidgetGroupWindow::destroyOrHideIfEmpty()
if (!wasHidden)
dw->show();
}
-#if QT_CONFIG(tabbar)
- const auto tabBars = findChildren<QTabBar *>(Qt::FindDirectChildrenOnly);
- for (QTabBar *tb : tabBars)
- tb->setParent(parentWidget());
-#endif
deleteLater();
}
+/*!
+ \internal
+ \return \c true if the group window has at least one visible QDockWidget child,
+ otherwise false.
+ */
+bool QDockWidgetGroupWindow::hasVisibleDockWidgets() const
+{
+ const auto &children = findChildren<QDockWidget *>(Qt::FindChildrenRecursively);
+ for (auto child : children) {
+ // WA_WState_Visible is set on the dock widget, associated to the active tab
+ // and unset on all others.
+ // WA_WState_Hidden is set if the dock widgets have been explicitly hidden.
+ // This is the relevant information to check (equivalent to !child->isHidden()).
+ if (!child->testAttribute(Qt::WA_WState_Hidden))
+ return true;
+ }
+ return false;
+}
+
/*! \internal
Sets the flags of this window in accordance to the capabilities of the dock widgets
*/
@@ -474,7 +534,7 @@ void QDockWidgetGroupWindow::adjustFlags()
m_removedFrameSize = QSize();
}
- show(); // setWindowFlags hides the window
+ setVisible(hasVisibleDockWidgets());
}
QWidget *titleBarOf = top ? top : parentWidget();
@@ -535,8 +595,12 @@ bool QDockWidgetGroupWindow::hover(QLayoutItem *widgetItem, const QPoint &mouseP
auto newGapPos = newState.gapIndex(mousePos, nestingEnabled, tabMode);
Q_ASSERT(!newGapPos.isEmpty());
- if (newGapPos == currentGapPos)
- return false; // gap is already there
+
+ // Do not insert a new gap item, if the current position already is a gap,
+ // or if the group window contains one
+ if (newGapPos == currentGapPos || newState.hasGapItem(newGapPos))
+ return false;
+
currentGapPos = newGapPos;
newState.insertGap(currentGapPos, widgetItem);
newState.fitItems();
@@ -583,6 +647,108 @@ void QDockWidgetGroupWindow::apply()
layoutInfo()->apply(false);
}
+void QDockWidgetGroupWindow::childEvent(QChildEvent *event)
+{
+ switch (event->type()) {
+ case QEvent::ChildRemoved:
+ if (auto *dockWidget = qobject_cast<QDockWidget *>(event->child()))
+ dockWidget->removeEventFilter(this);
+ destroyIfSingleItemLeft();
+ break;
+ case QEvent::ChildAdded:
+ if (auto *dockWidget = qobject_cast<QDockWidget *>(event->child()))
+ dockWidget->installEventFilter(this);
+ break;
+ default:
+ break;
+ }
+}
+
+bool QDockWidgetGroupWindow::eventFilter(QObject *obj, QEvent *event)
+{
+ auto *dockWidget = qobject_cast<QDockWidget *>(obj);
+ if (!dockWidget)
+ return QWidget::eventFilter(obj, event);
+
+ switch (event->type()) {
+ case QEvent::Close:
+ // We don't want closed dock widgets in a floating tab
+ // => dock it to the main dock, before closing;
+ reparent(dockWidget);
+ dockWidget->setFloating(false);
+ break;
+
+ case QEvent::Hide:
+ // if the dock widget is not an active tab, it is hidden anyway.
+ // if it is the active tab, hide the whole group.
+ if (dockWidget->isVisible())
+ hide();
+ break;
+
+ default:
+ break;
+ }
+ return QWidget::eventFilter(obj, event);
+}
+
+void QDockWidgetGroupWindow::destroyIfSingleItemLeft()
+{
+ const auto &dockWidgets = this->dockWidgets();
+
+ // Handle only the last dock
+ if (dockWidgets.count() != 1)
+ return;
+
+ auto *lastDockWidget = dockWidgets.at(0);
+
+ // If the last remaining dock widget is not in the group window's item_list,
+ // a group window is being docked on a main window docking area.
+ // => don't interfere
+ if (layoutInfo()->indexOf(lastDockWidget).isEmpty())
+ return;
+
+ auto *mainWindow = qobject_cast<QMainWindow *>(parentWidget());
+ QMainWindowLayout *mwLayout = qt_mainwindow_layout(mainWindow);
+
+ // Unplug the last remaining dock widget and hide the group window, to avoid flickering
+ mwLayout->unplug(lastDockWidget, QDockWidgetPrivate::DragScope::Widget);
+ lastDockWidget->setGeometry(geometry());
+ hide();
+
+ // Get the layout info for the main window dock, where dock widgets need to go
+ QDockAreaLayoutInfo &parentInfo = mwLayout->layoutState.dockAreaLayout.docks[layoutInfo()->dockPos];
+
+ // Re-parent last dock widget
+ reparent(lastDockWidget);
+
+ // the group window could still have placeholder items => clear everything
+ layoutInfo()->item_list.clear();
+
+ // remove the group window and the dock's item_list pointing to it.
+ parentInfo.remove(this);
+ destroyOrHideIfEmpty();
+}
+
+void QDockWidgetGroupWindow::reparent(QDockWidget *dockWidget)
+{
+ // reparent a dockWidget to the main window
+ // - remove it from the floating dock's layout info
+ // - insert it to the main dock's layout info
+ // Finally, set draggingDock to nullptr, since the drag is finished.
+ auto *mainWindow = qobject_cast<QMainWindow *>(parentWidget());
+ Q_ASSERT(mainWindow);
+ QMainWindowLayout *mwLayout = qt_mainwindow_layout(mainWindow);
+ Q_ASSERT(mwLayout);
+ QDockAreaLayoutInfo &parentInfo = mwLayout->layoutState.dockAreaLayout.docks[layoutInfo()->dockPos];
+ dockWidget->removeEventFilter(this);
+ parentInfo.add(dockWidget);
+ layoutInfo()->remove(dockWidget);
+ const bool wasFloating = dockWidget->isFloating();
+ const bool wasVisible = dockWidget->isVisible();
+ dockWidget->setParent(mainWindow);
+ dockWidget->setFloating(wasFloating);
+ dockWidget->setVisible(wasVisible);
+}
#endif
/******************************************************************************
@@ -1195,7 +1361,7 @@ bool QMainWindowLayoutState::restoreState(QDataStream &_stream,
if (info == nullptr) {
continue;
}
- info->item_list.append(QDockAreaLayoutItem(new QDockWidgetItem(w)));
+ info->add(w);
}
}
}
@@ -1265,17 +1431,18 @@ bool QMainWindowLayoutState::restoreState(QDataStream &_stream,
#if QT_CONFIG(toolbar)
-static inline void validateToolBarArea(Qt::ToolBarArea &area)
+static constexpr Qt::ToolBarArea validateToolBarArea(Qt::ToolBarArea area)
{
switch (area) {
case Qt::LeftToolBarArea:
case Qt::RightToolBarArea:
case Qt::TopToolBarArea:
case Qt::BottomToolBarArea:
- break;
+ return area;
default:
- area = Qt::TopToolBarArea;
+ break;
}
+ return Qt::TopToolBarArea;
}
static QInternal::DockPosition toDockPos(Qt::ToolBarArea area)
@@ -1311,7 +1478,7 @@ static inline Qt::ToolBarArea toToolBarArea(int pos)
void QMainWindowLayout::addToolBarBreak(Qt::ToolBarArea area)
{
- validateToolBarArea(area);
+ area = validateToolBarArea(area);
layoutState.toolBarAreaLayout.addToolBarBreak(toDockPos(area));
if (savedState.isValid())
@@ -1365,7 +1532,7 @@ void QMainWindowLayout::addToolBar(Qt::ToolBarArea area,
QToolBar *toolbar,
bool)
{
- validateToolBarArea(area);
+ area = validateToolBarArea(area);
// let's add the toolbar to the layout
addChildWidget(toolbar);
QLayoutItem *item = layoutState.toolBarAreaLayout.addToolBar(toDockPos(area), toolbar);
@@ -1467,7 +1634,6 @@ inline static Qt::DockWidgetArea toDockWidgetArea(int pos)
// Checks if QDockWidgetGroupWindow or QDockWidget can be plugged the area indicated by path.
// Returns false if called with invalid widget type or if compiled without dockwidget support.
-#if QT_CONFIG(dockwidget)
static bool isAreaAllowed(QWidget *widget, const QList<int> &path)
{
Q_ASSERT_X((path.size() > 1), "isAreaAllowed", "invalid path size");
@@ -1502,7 +1668,6 @@ static bool isAreaAllowed(QWidget *widget, const QList<int> &path)
qCDebug(lcQpaDockWidgets) << "Docking requested for invalid widget type (coding error)." << widget << area;
return false;
}
-#endif
void QMainWindowLayout::setCorner(Qt::Corner corner, Qt::DockWidgetArea area)
{
@@ -1522,7 +1687,6 @@ Qt::DockWidgetArea QMainWindowLayout::corner(Qt::Corner corner) const
// Returns the rectangle of a dockWidgetArea
// if max is true, the maximum possible rectangle for dropping is returned
// the current visible rectangle otherwise
-#if QT_CONFIG(dockwidget)
QRect QMainWindowLayout::dockWidgetAreaRect(const Qt::DockWidgetArea area, DockWidgetAreaSize size) const
{
const QInternal::DockPosition dockPosition = toDockPos(area);
@@ -1538,7 +1702,6 @@ QRect QMainWindowLayout::dockWidgetAreaRect(const Qt::DockWidgetArea area, DockW
// Return maximum or visible rectangle
return (size == Maximum) ? dl.gapRect(dockPosition) : dl.docks[dockPosition].rect;
}
-#endif
void QMainWindowLayout::addDockWidget(Qt::DockWidgetArea area,
QDockWidget *dockwidget,
@@ -1569,6 +1732,7 @@ bool QMainWindowLayout::restoreDockWidget(QDockWidget *dockwidget)
#if QT_CONFIG(tabbar)
void QMainWindowLayout::tabifyDockWidget(QDockWidget *first, QDockWidget *second)
{
+ applyRestoredState();
addChildWidget(second);
layoutState.dockAreaLayout.tabifyDockWidget(first, second);
emit second->dockLocationChanged(dockWidgetArea(first));
@@ -1690,6 +1854,7 @@ void QMainWindowLayout::splitDockWidget(QDockWidget *after,
QDockWidget *dockwidget,
Qt::Orientation orientation)
{
+ applyRestoredState();
addChildWidget(dockwidget);
layoutState.dockAreaLayout.splitDockWidget(after, dockwidget, orientation);
emit dockwidget->dockLocationChanged(dockWidgetArea(after));
@@ -1714,10 +1879,15 @@ void QMainWindowLayout::keepSize(QDockWidget *w)
// Handle custom tooltip, and allow to drag tabs away.
class QMainWindowTabBar : public QTabBar
{
+ Q_OBJECT
QMainWindow *mainWindow;
QPointer<QDockWidget> draggingDock; // Currently dragging (detached) dock widget
public:
QMainWindowTabBar(QMainWindow *parent);
+ ~QMainWindowTabBar();
+ QDockWidget *dockAt(int index) const;
+ QList<QDockWidget *> dockWidgets() const;
+ bool contains(const QDockWidget *dockWidget) const;
protected:
bool event(QEvent *e) override;
void mouseReleaseEvent(QMouseEvent*) override;
@@ -1725,12 +1895,65 @@ protected:
};
+QMainWindowTabBar *QMainWindowLayout::findTabBar(const QDockWidget *dockWidget) const
+{
+ for (auto *bar : usedTabBars) {
+ Q_ASSERT(qobject_cast<QMainWindowTabBar *>(bar));
+ auto *tabBar = static_cast<QMainWindowTabBar *>(bar);
+ if (tabBar->contains(dockWidget))
+ return tabBar;
+ }
+ return nullptr;
+}
+
QMainWindowTabBar::QMainWindowTabBar(QMainWindow *parent)
: QTabBar(parent), mainWindow(parent)
{
setExpanding(false);
}
+QList<QDockWidget *> QMainWindowTabBar::dockWidgets() const
+{
+ QList<QDockWidget *> docks;
+ for (int i = 0; i < count(); ++i) {
+ if (QDockWidget *dock = dockAt(i))
+ docks << dock;
+ }
+ return docks;
+}
+
+bool QMainWindowTabBar::contains(const QDockWidget *dockWidget) const
+{
+ for (int i = 0; i < count(); ++i) {
+ if (dockAt(i) == dockWidget)
+ return true;
+ }
+ return false;
+}
+
+// When a dock widget is removed from a floating tab,
+// Events need to be processed for the tab bar to realize that the dock widget is gone.
+// In this case count() counts the dock widget in transition and accesses dockAt
+// with an out-of-bounds index.
+// => return nullptr in contrast to other xxxxxAt() functions
+QDockWidget *QMainWindowTabBar::dockAt(int index) const
+{
+ QMainWindowTabBar *that = const_cast<QMainWindowTabBar *>(this);
+ QMainWindowLayout* mlayout = qt_mainwindow_layout(mainWindow);
+ QDockAreaLayoutInfo *info = mlayout->dockInfo(that);
+ if (!info)
+ return nullptr;
+
+ const int itemIndex = info->tabIndexToListIndex(index);
+ if (itemIndex >= 0) {
+ Q_ASSERT(itemIndex < info->item_list.count());
+ const QDockAreaLayoutItem &item = info->item_list.at(itemIndex);
+ return item.widgetItem ? qobject_cast<QDockWidget *>(item.widgetItem->widget()) : nullptr;
+ }
+
+ return nullptr;
+}
+
void QMainWindowTabBar::mouseMoveEvent(QMouseEvent *e)
{
// The QTabBar handles the moving (reordering) of tabs.
@@ -1744,13 +1967,8 @@ void QMainWindowTabBar::mouseMoveEvent(QMouseEvent *e)
offset *= 3;
QRect r = rect().adjusted(-offset, -offset, offset, offset);
if (d->dragInProgress && !r.contains(e->position().toPoint()) && d->validIndex(d->pressedIndex)) {
- QMainWindowLayout* mlayout = qt_mainwindow_layout(mainWindow);
- QDockAreaLayoutInfo *info = mlayout->dockInfo(this);
- Q_ASSERT(info);
- int idx = info->tabIndexToListIndex(d->pressedIndex);
- const QDockAreaLayoutItem &item = info->item_list.at(idx);
- if (item.widgetItem
- && (draggingDock = qobject_cast<QDockWidget *>(item.widgetItem->widget()))) {
+ draggingDock = dockAt(d->pressedIndex);
+ if (draggingDock) {
// We should drag this QDockWidget away by unpluging it.
// First cancel the QTabBar's internal move
d->moveTabFinished(d->pressedIndex);
@@ -1763,7 +1981,7 @@ void QMainWindowTabBar::mouseMoveEvent(QMouseEvent *e)
QDockWidgetPrivate *dockPriv = static_cast<QDockWidgetPrivate *>(QObjectPrivate::get(draggingDock));
QDockWidgetLayout *dwlayout = static_cast<QDockWidgetLayout *>(draggingDock->layout());
dockPriv->initDrag(dwlayout->titleArea().center(), true);
- dockPriv->startDrag(false);
+ dockPriv->startDrag(QDockWidgetPrivate::DragScope::Widget);
if (dockPriv->state)
dockPriv->state->ctrlDrag = e->modifiers() & Qt::ControlModifier;
}
@@ -1781,13 +1999,28 @@ void QMainWindowTabBar::mouseMoveEvent(QMouseEvent *e)
QTabBar::mouseMoveEvent(e);
}
+QMainWindowTabBar::~QMainWindowTabBar()
+{
+ if (!mainWindow || mainWindow == parentWidget())
+ return;
+
+ // tab bar is not parented to the main window
+ // => can only be a dock widget group window
+ // => remove itself from used and unused tab bar containers
+ auto *mwLayout = qt_mainwindow_layout(mainWindow);
+ if (!mwLayout)
+ return;
+ mwLayout->unusedTabBars.removeOne(this);
+ mwLayout->usedTabBars.remove(this);
+}
+
void QMainWindowTabBar::mouseReleaseEvent(QMouseEvent *e)
{
if (draggingDock && e->button() == Qt::LeftButton) {
QDockWidgetPrivate *dockPriv = static_cast<QDockWidgetPrivate *>(QObjectPrivate::get(draggingDock));
- if (dockPriv->state && dockPriv->state->dragging) {
- dockPriv->endDrag();
- }
+ if (dockPriv->state && dockPriv->state->dragging)
+ dockPriv->endDrag(QDockWidgetPrivate::EndDragMode::LocationChange);
+
draggingDock = nullptr;
}
QTabBar::mouseReleaseEvent(e);
@@ -1811,9 +2044,31 @@ bool QMainWindowTabBar::event(QEvent *e)
return true;
}
+QList<QDockWidget *> QMainWindowLayout::tabifiedDockWidgets(const QDockWidget *dockWidget) const
+{
+ const auto *bar = findTabBar(dockWidget);
+ if (!bar)
+ return {};
+
+ QList<QDockWidget *> buddies = bar->dockWidgets();
+ // Return only other dock widgets associated with dockWidget in a tab bar.
+ // If dockWidget is alone in a tab bar, return an empty list.
+ buddies.removeOne(dockWidget);
+ return buddies;
+}
+
+bool QMainWindowLayout::isDockWidgetTabbed(const QDockWidget *dockWidget) const
+{
+ // A single dock widget in a tab bar is not considered to be tabbed.
+ // This is to make sure, we don't drag an empty QDockWidgetGroupWindow around.
+ // => only consider tab bars with two or more tabs.
+ const auto *bar = findTabBar(dockWidget);
+ return bar && bar->count() > 1;
+}
+
QTabBar *QMainWindowLayout::getTabBar()
{
- if (!usedTabBars.isEmpty()) {
+ if (!usedTabBars.isEmpty() && !isInRestoreState) {
/*
If dock widgets have been removed and added while the main window was
hidden, then the layout hasn't been activated yet, and tab bars from empty
@@ -1985,9 +2240,37 @@ QLayoutItem *QMainWindowLayout::takeAt(int index)
return nullptr;
}
+
+/*!
+ \internal
+
+ restoredState stores what we earlier read from storage, but it couldn't
+ be applied as the mainwindow wasn't large enough (yet) to fit the state.
+ Usually, the restored state would be applied lazily in setGeometry below.
+ However, if the mainwindow's layout is modified (e.g. by a call to tabify or
+ splitDockWidgets), then we have to forget the restored state as it might contain
+ dangling pointers (QDockWidgetLayoutItem has a copy constructor that copies the
+ layout item pointer, and splitting or tabify might have to delete some of those
+ layout structures).
+
+ Functions that might result in the QMainWindowLayoutState storing dangling pointers
+ have to call this function first, so that the restoredState becomes the actual state
+ first, and is forgotten afterwards.
+*/
+void QMainWindowLayout::applyRestoredState()
+{
+ if (restoredState) {
+ layoutState = *restoredState;
+ restoredState.reset();
+ discardRestoredStateTimer.stop();
+ }
+}
+
void QMainWindowLayout::setGeometry(const QRect &_r)
{
- if (savedState.isValid())
+ // Check if the state is valid, and avoid replacing it again if it is currently used
+ // in applyState
+ if (savedState.isValid() || (restoredState && isInApplyState))
return;
QRect r = _r;
@@ -2523,25 +2806,25 @@ static QTabBar::Shape tabwidgetPositionToTabBarShape(QWidget *w)
Returns the QLayoutItem of the dragged element.
The layout item is kept in the layout but set as a gap item.
*/
-QLayoutItem *QMainWindowLayout::unplug(QWidget *widget, bool group)
+QLayoutItem *QMainWindowLayout::unplug(QWidget *widget, QDockWidgetPrivate::DragScope scope)
{
#if QT_CONFIG(dockwidget) && QT_CONFIG(tabwidget)
auto *groupWindow = qobject_cast<const QDockWidgetGroupWindow *>(widget->parentWidget());
if (!widget->isWindow() && groupWindow) {
- if (group && groupWindow->tabLayoutInfo()) {
+ if (scope == QDockWidgetPrivate::DragScope::Group && groupWindow->tabLayoutInfo()) {
// We are just dragging a floating window as it, not need to do anything, we just have to
// look up the corresponding QWidgetItem* if it exists
if (QDockAreaLayoutInfo *info = dockInfo(widget->parentWidget())) {
QList<int> groupWindowPath = info->indexOf(widget->parentWidget());
return groupWindowPath.isEmpty() ? nullptr : info->item(groupWindowPath).widgetItem;
}
- qCDebug(lcQpaDockWidgets) << "Drag only:" << widget << "Group:" << group;
+ qCDebug(lcQpaDockWidgets) << "Drag only:" << widget << "Group:" << (scope == QDockWidgetPrivate::DragScope::Group);
return nullptr;
}
QList<int> path = groupWindow->layoutInfo()->indexOf(widget);
QDockAreaLayoutItem parentItem = groupWindow->layoutInfo()->item(path);
QLayoutItem *item = parentItem.widgetItem;
- if (group && path.size() > 1
+ if (scope == QDockWidgetPrivate::DragScope::Group && path.size() > 1
&& unplugGroup(this, &item, parentItem)) {
qCDebug(lcQpaDockWidgets) << "Unplugging:" << widget << "from" << item;
return item;
@@ -2549,73 +2832,10 @@ QLayoutItem *QMainWindowLayout::unplug(QWidget *widget, bool group)
// We are unplugging a single dock widget from a floating window.
QDockWidget *dockWidget = qobject_cast<QDockWidget *>(widget);
Q_ASSERT(dockWidget); // cannot be a QDockWidgetGroupWindow because it's not floating.
-
- // unplug the widget first
dockWidget->d_func()->unplug(widget->geometry());
- // Create a floating tab, copy properties and generate layout info
- QDockWidgetGroupWindow *floatingTabs = createTabbedDockWindow();
- const QInternal::DockPosition dockPos = groupWindow->layoutInfo()->dockPos;
- QDockAreaLayoutInfo *info = floatingTabs->layoutInfo();
-
- const QTabBar::Shape shape = tabwidgetPositionToTabBarShape(dockWidget);
-
- // Populate newly created DockAreaLayoutInfo of floating tabs
- *info = QDockAreaLayoutInfo(&layoutState.dockAreaLayout.sep, dockPos,
- Qt::Horizontal, shape,
- layoutState.mainWindow);
-
- // Create tab and hide it as group window contains only one widget
- info->tabbed = true;
- info->tabBar = getTabBar();
- info->tabBar->hide();
- updateGapIndicator();
-
- // Reparent it to a QDockWidgetGroupLayout
- floatingTabs->setGeometry(dockWidget->geometry());
-
- // Append reference to floatingTabs to the dock's item_list
- parentItem.widgetItem = new QDockWidgetGroupWindowItem(floatingTabs);
- layoutState.dockAreaLayout.docks[dockPos].item_list.append(parentItem);
-
- // use populated parentItem to set reference to dockWidget as the first item in own list
- parentItem.widgetItem = new QDockWidgetItem(dockWidget);
- info->item_list = {parentItem};
-
- // Add non-gap items of the dock to the tab bar
- for (const auto &listItem : layoutState.dockAreaLayout.docks[dockPos].item_list) {
- if (listItem.GapItem || !listItem.widgetItem)
- continue;
- info->tabBar->addTab(listItem.widgetItem->widget()->objectName());
- }
-
- // Re-parent and fit
- floatingTabs->setParent(layoutState.mainWindow);
- floatingTabs->layoutInfo()->fitItems();
- floatingTabs->layoutInfo()->apply(dockOptions & QMainWindow::AnimatedDocks);
- groupWindow->layoutInfo()->fitItems();
- groupWindow->layoutInfo()->apply(dockOptions & QMainWindow::AnimatedDocks);
- dockWidget->d_func()->tabPosition = layoutState.mainWindow->tabPosition(toDockWidgetArea(dockPos));
- info->reparentWidgets(floatingTabs);
- dockWidget->setParent(floatingTabs);
- info->updateTabBar();
-
- // Show the new item
- const QList<int> path = layoutState.indexOf(floatingTabs);
- QRect r = layoutState.itemRect(path);
- savedState = layoutState;
- savedState.fitLayout();
-
- // Update gap, fix orientation, raise and show
- currentGapPos = path;
- currentGapRect = r;
- updateGapIndicator();
- fixToolBarOrientation(parentItem.widgetItem, currentGapPos.at(1));
- floatingTabs->show();
- floatingTabs->raise();
-
qCDebug(lcQpaDockWidgets) << "Unplugged from floating dock:" << widget << "from" << parentItem.widgetItem;
- return parentItem.widgetItem;
+ return item;
}
}
#endif
@@ -2634,7 +2854,7 @@ QLayoutItem *QMainWindowLayout::unplug(QWidget *widget, bool group)
if (QDockWidget *dw = qobject_cast<QDockWidget*>(widget)) {
Q_ASSERT(path.constFirst() == 1);
#if QT_CONFIG(tabwidget)
- if (group && (dockOptions & QMainWindow::GroupedDragging) && path.size() > 3
+ if (scope == QDockWidgetPrivate::DragScope::Group && (dockOptions & QMainWindow::GroupedDragging) && path.size() > 3
&& unplugGroup(this, &item,
layoutState.dockAreaLayout.item(path.mid(1, path.size() - 2)))) {
path.removeLast();
@@ -2665,7 +2885,7 @@ QLayoutItem *QMainWindowLayout::unplug(QWidget *widget, bool group)
const auto *layout = qobject_cast<QDockWidgetLayout *>(dw->layout());
const bool verticalTitleBar = layout ? layout->verticalTitleBar : false;
const int tbHeight = QApplication::style()
- ? QApplication::style()->pixelMetric(QStyle::PixelMetric::PM_TitleBarHeight)
+ ? QApplication::style()->pixelMetric(QStyle::PixelMetric::PM_TitleBarHeight, nullptr, dw)
: 20;
const int minHeight = verticalTitleBar ? 2 * tbHeight : tbHeight;
const int minWidth = verticalTitleBar ? tbHeight : 2 * tbHeight;
@@ -2683,7 +2903,7 @@ QLayoutItem *QMainWindowLayout::unplug(QWidget *widget, bool group)
#endif
#if !QT_CONFIG(dockwidget) || !QT_CONFIG(tabbar)
- Q_UNUSED(group);
+ Q_UNUSED(scope);
#endif
layoutState.unplug(path ,&savedState);
@@ -2751,6 +2971,7 @@ void QMainWindowLayout::hover(QLayoutItem *hoverTarget,
QWidget *widget = hoverTarget->widget();
#if QT_CONFIG(dockwidget)
+ widget->raise();
if ((dockOptions & QMainWindow::GroupedDragging) && (qobject_cast<QDockWidget*>(widget)
|| qobject_cast<QDockWidgetGroupWindow *>(widget))) {
@@ -2767,7 +2988,7 @@ void QMainWindowLayout::hover(QLayoutItem *hoverTarget,
continue;
// Check permission to dock on another dock widget or floating dock
- // FIXME in 6.4
+ // FIXME in Qt 7
if (w != widget && w->isWindow() && w->isVisible() && !w->isMinimized())
candidates << w;
@@ -2805,16 +3026,26 @@ void QMainWindowLayout::hover(QLayoutItem *hoverTarget,
floatingTabs->setGeometry(dropTo->geometry());
QDockAreaLayoutInfo *info = floatingTabs->layoutInfo();
const QTabBar::Shape shape = tabwidgetPositionToTabBarShape(dropTo);
- const QInternal::DockPosition dockPosition = toDockPos(dockWidgetArea(dropTo));
+
+ // dropTo and widget may be in a state where they transition
+ // from being a group window child to a single floating dock widget.
+ // In that case, their path to a main window dock may not have been
+ // updated yet.
+ // => ask both and fall back to dock 1 (right dock)
+ QInternal::DockPosition dockPosition = toDockPos(dockWidgetArea(dropTo));
+ if (dockPosition == QInternal::DockPosition::DockCount)
+ dockPosition = toDockPos(dockWidgetArea(widget));
+ if (dockPosition == QInternal::DockPosition::DockCount)
+ dockPosition = QInternal::DockPosition::RightDock;
+
*info = QDockAreaLayoutInfo(&layoutState.dockAreaLayout.sep, dockPosition,
Qt::Horizontal, shape,
static_cast<QMainWindow *>(parentWidget()));
info->tabBar = getTabBar();
info->tabbed = true;
- QLayout *parentLayout = dropTo->parentWidget()->layout();
- info->item_list.append(
- QDockAreaLayoutItem(parentLayout->takeAt(parentLayout->indexOf(dropTo))));
-
+ info->add(dropTo);
+ QDockAreaLayoutInfo &parentInfo = layoutState.dockAreaLayout.docks[dockPosition];
+ parentInfo.add(floatingTabs);
dropTo->setParent(floatingTabs);
qCDebug(lcQpaDockWidgets) << "Wrapping" << widget << "into floating tabs" << floatingTabs;
w = floatingTabs;
@@ -2827,15 +3058,21 @@ void QMainWindowLayout::hover(QLayoutItem *hoverTarget,
qCDebug(lcQpaDockWidgets) << "Raising" << widget;
}
#endif
- auto group = qobject_cast<QDockWidgetGroupWindow *>(w);
- Q_ASSERT(group);
- if (group->hover(hoverTarget, group->mapFromGlobal(mousePos))) {
- setCurrentHoveredFloat(group);
+ auto *groupWindow = qobject_cast<QDockWidgetGroupWindow *>(w);
+ Q_ASSERT(groupWindow);
+ if (groupWindow->hover(hoverTarget, groupWindow->mapFromGlobal(mousePos))) {
+ setCurrentHoveredFloat(groupWindow);
applyState(layoutState); // update the tabbars
}
return;
}
}
+
+ // If a temporary group window has been created during a hover,
+ // remove it, if it has only one dockwidget child
+ if (currentHoveredFloat)
+ currentHoveredFloat->destroyIfSingleItemLeft();
+
setCurrentHoveredFloat(nullptr);
layoutState.dockAreaLayout.fallbackToSizeHints = false;
#endif // QT_CONFIG(dockwidget)
@@ -2960,6 +3197,7 @@ void QMainWindowLayout::saveState(QDataStream &stream) const
bool QMainWindowLayout::restoreState(QDataStream &stream)
{
+ QScopedValueRollback<bool> guard(isInRestoreState, true);
savedState = layoutState;
layoutState.clear();
layoutState.rect = savedState.rect;
@@ -3005,6 +3243,42 @@ bool QMainWindowLayout::restoreState(QDataStream &stream)
return true;
}
+#if QT_CONFIG(draganddrop)
+bool QMainWindowLayout::needsPlatformDrag()
+{
+ static const bool wayland =
+ QGuiApplication::platformName().startsWith("wayland"_L1, Qt::CaseInsensitive);
+ return wayland;
+}
+
+Qt::DropAction QMainWindowLayout::performPlatformWidgetDrag(QLayoutItem *widgetItem,
+ const QPoint &pressPosition)
+{
+ draggingWidget = widgetItem;
+ QWidget *widget = widgetItem->widget();
+ auto drag = QDrag(widget);
+ auto mimeData = new QMimeData();
+ auto window = widgetItem->widget()->windowHandle();
+
+ auto serialize = [](const auto &object) {
+ QByteArray data;
+ QDataStream dataStream(&data, QIODevice::WriteOnly);
+ dataStream << object;
+ return data;
+ };
+ mimeData->setData("application/x-qt-mainwindowdrag-window"_L1,
+ serialize(reinterpret_cast<qintptr>(window)));
+ mimeData->setData("application/x-qt-mainwindowdrag-position"_L1, serialize(pressPosition));
+ drag.setMimeData(mimeData);
+
+ auto result = drag.exec();
+
+ draggingWidget = nullptr;
+ return result;
+}
+#endif
+
QT_END_NAMESPACE
+#include "qmainwindowlayout.moc"
#include "moc_qmainwindowlayout_p.cpp"
diff --git a/src/widgets/widgets/qmainwindowlayout_p.h b/src/widgets/widgets/qmainwindowlayout_p.h
index 394a052a09..55a27e4849 100644
--- a/src/widgets/widgets/qmainwindowlayout_p.h
+++ b/src/widgets/widgets/qmainwindowlayout_p.h
@@ -1,8 +1,8 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#ifndef QDYNAMICMAINWINDOWLAYOUT_P_H
-#define QDYNAMICMAINWINDOWLAYOUT_P_H
+#ifndef QMAINWINDOWLAYOUT_P_H
+#define QMAINWINDOWLAYOUT_P_H
//
// W A R N I N G
@@ -29,15 +29,24 @@
#include "QtCore/qset.h"
#include "private/qlayoutengine_p.h"
#include "private/qwidgetanimator_p.h"
-
#if QT_CONFIG(dockwidget)
+#include "private/qdockwidget_p.h"
+
#include "qdockarealayout_p.h"
#include "qdockwidget.h"
+#else
+struct QDockWidgetPrivate {
+ enum class DragScope {
+ Group
+ };
+};
#endif
#if QT_CONFIG(toolbar)
#include "qtoolbararealayout_p.h"
#endif
+
#include <QtCore/qloggingcategory.h>
+#include <QtCore/qpointer.h>
QT_REQUIRE_CONFIG(mainwindow);
@@ -306,14 +315,17 @@ class Q_AUTOTEST_EXPORT QDockWidgetGroupWindow : public QWidget
{
Q_OBJECT
public:
- explicit QDockWidgetGroupWindow(QWidget* parent = nullptr, Qt::WindowFlags f = { })
- : QWidget(parent, f) {}
+ explicit QDockWidgetGroupWindow(QWidget *parent = nullptr, Qt::WindowFlags f = {})
+ : QWidget(parent, f)
+ {
+ }
QDockAreaLayoutInfo *layoutInfo() const;
#if QT_CONFIG(tabbar)
const QDockAreaLayoutInfo *tabLayoutInfo() const;
QDockWidget *activeTabbedDockWidget() const;
#endif
void destroyOrHideIfEmpty();
+ bool hasVisibleDockWidgets() const;
void adjustFlags();
bool hasNativeDecos() const;
@@ -321,6 +333,10 @@ public:
void updateCurrentGapRect();
void restore();
void apply();
+ void childEvent(QChildEvent *event) override;
+ void reparent(QDockWidget *dockWidget);
+ void destroyIfSingleItemLeft();
+ QList<QDockWidget *> dockWidgets() const { return findChildren<QDockWidget *>(); }
QRect currentGapRect;
QList<int> currentGapPos;
@@ -330,6 +346,7 @@ signals:
protected:
bool event(QEvent *) override;
+ bool eventFilter(QObject *obj, QEvent *event) override;
void paintEvent(QPaintEvent*) override;
private:
@@ -433,6 +450,7 @@ public:
bool restoreState(QDataStream &stream, const QMainWindowLayoutState &oldState);
};
+class QMainWindowTabBar;
class Q_AUTOTEST_EXPORT QMainWindowLayout
: public QLayout,
public QMainWindowLayoutSeparatorHelper<QMainWindowLayout>
@@ -560,17 +578,29 @@ public:
#if QT_CONFIG(dockwidget)
QPointer<QDockWidgetGroupWindow> currentHoveredFloat; // set when dragging over a floating dock widget
void setCurrentHoveredFloat(QDockWidgetGroupWindow *w);
+#if QT_CONFIG(tabbar)
+ bool isDockWidgetTabbed(const QDockWidget *dockWidget) const;
+ QList<QDockWidget *> tabifiedDockWidgets(const QDockWidget *dockWidget) const;
+ QMainWindowTabBar *findTabBar(const QDockWidget *dockWidget) const;
+#endif
#endif
bool isInApplyState = false;
void hover(QLayoutItem *hoverTarget, const QPoint &mousePos);
bool plug(QLayoutItem *widgetItem);
- QLayoutItem *unplug(QWidget *widget, bool group = false);
+ QLayoutItem *unplug(QWidget *widget, QDockWidgetPrivate::DragScope scope);
void revert(QLayoutItem *widgetItem);
void applyState(QMainWindowLayoutState &newState, bool animate = true);
+ void applyRestoredState();
void restore(bool keepSavedState = false);
void animationFinished(QWidget *widget);
+#if QT_CONFIG(draganddrop)
+ static bool needsPlatformDrag();
+ Qt::DropAction performPlatformWidgetDrag(QLayoutItem *widgetItem, const QPoint &pressPosition);
+ QLayoutItem *draggingWidget = nullptr;
+#endif
+
protected:
void timerEvent(QTimerEvent *e) override;
@@ -586,6 +616,7 @@ private:
#if QT_CONFIG(tabbar)
void updateTabBarShapes();
#endif
+ bool isInRestoreState = false;
};
#if QT_CONFIG(dockwidget) && !defined(QT_NO_DEBUG_STREAM)
@@ -596,4 +627,4 @@ QDebug operator<<(QDebug debug, const QMainWindowLayout *layout);
QT_END_NAMESPACE
-#endif // QDYNAMICMAINWINDOWLAYOUT_P_H
+#endif // QMAINWINDOWLAYOUT_P_H
diff --git a/src/widgets/widgets/qmdiarea.cpp b/src/widgets/widgets/qmdiarea.cpp
index 4bbb5c6254..79b83453ac 100644
--- a/src/widgets/widgets/qmdiarea.cpp
+++ b/src/widgets/widgets/qmdiarea.cpp
@@ -220,19 +220,7 @@ static inline QMdiArea *mdiAreaParent(QWidget *widget)
}
#if QT_CONFIG(tabwidget)
-QTabBar::Shape _q_tb_tabBarShapeFrom(QTabWidget::TabShape shape, QTabWidget::TabPosition position)
-{
- const bool rounded = (shape == QTabWidget::Rounded);
- if (position == QTabWidget::North)
- return rounded ? QTabBar::RoundedNorth : QTabBar::TriangularNorth;
- if (position == QTabWidget::South)
- return rounded ? QTabBar::RoundedSouth : QTabBar::TriangularSouth;
- if (position == QTabWidget::East)
- return rounded ? QTabBar::RoundedEast : QTabBar::TriangularEast;
- if (position == QTabWidget::West)
- return rounded ? QTabBar::RoundedWest : QTabBar::TriangularWest;
- return QTabBar::RoundedNorth;
-}
+QTabBar::Shape _q_tb_tabBarShapeFrom(QTabWidget::TabShape shape, QTabWidget::TabPosition position);
#endif // QT_CONFIG(tabwidget)
static inline QString tabTextFor(QMdiSubWindow *subWindow)
@@ -683,7 +671,11 @@ void QMdiAreaPrivate::_q_deactivateAllWindows(QMdiSubWindow *aboutToActivate)
aboutToBecomeActive = aboutToActivate;
Q_ASSERT(aboutToBecomeActive);
- foreach (QMdiSubWindow *child, childWindows) {
+ // Take a copy because child->showNormal() could indirectly call
+ // QCoreApplication::sendEvent(), which could call unknown code that e.g.
+ // recurses into the class modifying childWindows.
+ const auto subWindows = childWindows;
+ for (QMdiSubWindow *child : subWindows) {
if (!sanityCheck(child, "QMdiArea::deactivateAllWindows") || aboutToBecomeActive == child)
continue;
// We don't want to handle signals caused by child->showNormal().
@@ -858,7 +850,7 @@ void QMdiAreaPrivate::place(Placer *placer, QMdiSubWindow *child)
QList<QRect> rects;
rects.reserve(childWindows.size());
QRect parentRect = q->rect();
- foreach (QMdiSubWindow *window, childWindows) {
+ for (QMdiSubWindow *window : std::as_const(childWindows)) {
if (!sanityCheck(window, "QMdiArea::place") || window == child || !window->isVisibleTo(q)
|| !window->testAttribute(Qt::WA_Moved)) {
continue;
@@ -900,7 +892,7 @@ void QMdiAreaPrivate::rearrange(Rearranger *rearranger)
const bool reverseList = rearranger->type() == Rearranger::RegularTiler;
const QList<QMdiSubWindow *> subWindows = subWindowList(activationOrder, reverseList);
QSize minSubWindowSize;
- foreach (QMdiSubWindow *child, subWindows) {
+ for (QMdiSubWindow *child : subWindows) {
if (!sanityCheck(child, "QMdiArea::rearrange") || !child->isVisible())
continue;
if (rearranger->type() == Rearranger::IconTiler) {
@@ -1314,7 +1306,7 @@ bool QMdiAreaPrivate::lastWindowAboutToBeDestroyed() const
*/
void QMdiAreaPrivate::setChildActivationEnabled(bool enable, bool onlyNextActivationEvent) const
{
- foreach (QMdiSubWindow *subWindow, childWindows) {
+ for (QMdiSubWindow *subWindow : childWindows) {
if (!subWindow || !subWindow->isVisible())
continue;
if (onlyNextActivationEvent)
@@ -1336,7 +1328,11 @@ void QMdiAreaPrivate::scrollBarPolicyChanged(Qt::Orientation orientation, Qt::Sc
const QMdiSubWindow::SubWindowOption option = orientation == Qt::Horizontal ?
QMdiSubWindow::AllowOutsideAreaHorizontally : QMdiSubWindow::AllowOutsideAreaVertically;
const bool enable = policy != Qt::ScrollBarAlwaysOff;
- foreach (QMdiSubWindow *child, childWindows) {
+ // Take a copy because child->setOption() may indirectly call QCoreApplication::sendEvent(),
+ // the latter could call unknown code that could e.g. recurse into the class
+ // modifying childWindows.
+ const auto subWindows = childWindows;
+ for (QMdiSubWindow *child : subWindows) {
if (!sanityCheck(child, "QMdiArea::scrollBarPolicyChanged"))
continue;
child->setOption(option, enable);
@@ -1352,7 +1348,7 @@ QMdiAreaPrivate::subWindowList(QMdiArea::WindowOrder order, bool reversed) const
return list;
if (order == QMdiArea::CreationOrder) {
- foreach (QMdiSubWindow *child, childWindows) {
+ for (QMdiSubWindow *child : childWindows) {
if (!child)
continue;
if (!reversed)
@@ -1538,7 +1534,12 @@ void QMdiAreaPrivate::setViewMode(QMdiArea::ViewMode mode)
isSubWindowsTiled = false;
- foreach (QMdiSubWindow *subWindow, childWindows)
+ // Take a copy as tabBar->addTab() will (indirectly) create a connection between
+ // the tab close button clicked() signal and the _q_closeTab() slot, which may
+ // indirectly call QCoreApplication::sendEvent(), the latter could result in
+ // invoking unknown code that could e.g. recurse into the class modifying childWindows.
+ const auto subWindows = childWindows;
+ for (QMdiSubWindow *subWindow : subWindows)
tabBar->addTab(subWindow->windowIcon(), tabTextFor(subWindow));
QMdiSubWindow *current = q->currentSubWindow();
@@ -1860,7 +1861,11 @@ void QMdiArea::closeAllSubWindows()
return;
d->isSubWindowsTiled = false;
- foreach (QMdiSubWindow *child, d->childWindows) {
+ // Take a copy because the child->close() call below may end up indirectly calling
+ // QCoreApplication::send{Spontaneous}Event(), which may call unknown code that
+ // could e.g. recurse into the class modifying d->childWindows.
+ const auto subWindows = d->childWindows;
+ for (QMdiSubWindow *child : subWindows) {
if (!sanityCheck(child, "QMdiArea::closeAllSubWindows"))
continue;
child->close();
@@ -1999,7 +2004,11 @@ void QMdiArea::removeSubWindow(QWidget *widget)
}
bool found = false;
- foreach (QMdiSubWindow *child, d->childWindows) {
+ // Take a copy because child->setWidget(nullptr) will indirectly
+ // QCoreApplication::sendEvent(); the latter could call unknown code that could
+ // e.g. recurse into the class modifying d->childWindows.
+ const auto subWindows = d->childWindows;
+ for (QMdiSubWindow *child : subWindows) {
if (!sanityCheck(child, "QMdiArea::removeSubWindow"))
continue;
if (child->widget() == widget) {
@@ -2280,7 +2289,11 @@ void QMdiArea::resizeEvent(QResizeEvent *resizeEvent)
// Resize maximized views.
bool hasMaximizedSubWindow = false;
- foreach (QMdiSubWindow *child, d->childWindows) {
+ // Take a copy because child->resize() may call QCoreApplication::sendEvent()
+ // which may invoke unknown code, that could e.g. recurse into the class
+ // modifying d->childWindows.
+ const auto subWindows = d->childWindows;
+ for (QMdiSubWindow *child : subWindows) {
if (sanityCheck(child, "QMdiArea::resizeEvent") && child->isMaximized()
&& child->size() != resizeEvent->size()) {
auto realSize = resizeEvent->size();
@@ -2339,7 +2352,9 @@ void QMdiArea::showEvent(QShowEvent *showEvent)
Q_D(QMdiArea);
if (!d->pendingRearrangements.isEmpty()) {
bool skipPlacement = false;
- foreach (Rearranger *rearranger, d->pendingRearrangements) {
+ // Take a copy because d->rearrange() may modify d->pendingRearrangements
+ const auto pendingRearrange = d->pendingRearrangements;
+ for (Rearranger *rearranger : pendingRearrange) {
// If this is the case, we don't have to lay out pending child windows
// since the rearranger will find a placement for them.
if (rearranger->type() != Rearranger::IconTiler && !skipPlacement)
@@ -2353,9 +2368,19 @@ void QMdiArea::showEvent(QShowEvent *showEvent)
}
if (!d->pendingPlacements.isEmpty()) {
- foreach (QMdiSubWindow *window, d->pendingPlacements) {
+ // Nothing obvious in the loop body changes the container (in this code path)
+ // during iteration, this is calling into a non-const method that does change
+ // the container when called from other places. So take a copy anyway for good
+ // measure.
+ const auto copy = d->pendingPlacements;
+ for (QMdiSubWindow *window : copy) {
if (!window)
continue;
+ if (d->viewMode == TabbedView && window->d_func()->isActive && !d->active) {
+ d->showActiveWindowMaximized = true;
+ d->emitWindowActivated(window); // Also maximizes the window
+ continue;
+ }
if (!window->testAttribute(Qt::WA_Resized)) {
QSize newSize(window->sizeHint().boundedTo(viewport()->size()));
window->resize(newSize.expandedTo(qSmartMinSize(window)));
@@ -2489,12 +2514,16 @@ bool QMdiArea::event(QEvent *event)
d->isSubWindowsTiled = true;
}
break;
- case QEvent::WindowIconChange:
- foreach (QMdiSubWindow *window, d->childWindows) {
+ case QEvent::WindowIconChange: {
+ // Take a copy because QCoreApplication::sendEvent() may call unknown code,
+ // that may cause recursing into the class
+ const auto subWindows = d->childWindows;
+ for (QMdiSubWindow *window : subWindows) {
if (sanityCheck(window, "QMdiArea::WindowIconChange"))
QCoreApplication::sendEvent(window, event);
}
break;
+ }
case QEvent::Hide:
d->setActive(d->active, false, false);
d->setChildActivationEnabled(false);
@@ -2649,7 +2678,10 @@ void QMdiArea::setupViewport(QWidget *viewport)
Q_D(QMdiArea);
if (viewport)
viewport->setAttribute(Qt::WA_OpaquePaintEvent, d->background.isOpaque());
- foreach (QMdiSubWindow *child, d->childWindows) {
+ // Take a copy because the child->setParent() call below may call QCoreApplication::sendEvent()
+ // which may call unknown code that could e.g. recurse into the class modifying d->childWindows.
+ const auto subWindows = d->childWindows;
+ for (QMdiSubWindow *child : subWindows) {
if (!sanityCheck(child, "QMdiArea::setupViewport"))
continue;
child->setParent(viewport, child->windowFlags());
diff --git a/src/widgets/widgets/qmdiarea_p.h b/src/widgets/widgets/qmdiarea_p.h
index 2e66a31fbc..e57b9772ca 100644
--- a/src/widgets/widgets/qmdiarea_p.h
+++ b/src/widgets/widgets/qmdiarea_p.h
@@ -29,6 +29,8 @@ QT_REQUIRE_CONFIG(mdiarea);
#include <private/qmdisubwindow_p.h>
#include <private/qabstractscrollarea_p.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
namespace QMdi {
diff --git a/src/widgets/widgets/qmdisubwindow.h b/src/widgets/widgets/qmdisubwindow.h
index a34c0249ec..4227f02dac 100644
--- a/src/widgets/widgets/qmdisubwindow.h
+++ b/src/widgets/widgets/qmdisubwindow.h
@@ -101,6 +101,7 @@ private:
Q_PRIVATE_SLOT(d_func(), void _q_enterInteractiveMode())
Q_PRIVATE_SLOT(d_func(), void _q_processFocusChanged(QWidget *, QWidget *))
friend class QMdiAreaPrivate;
+ friend class QMdiArea;
#if QT_CONFIG(tabbar)
friend class QMdiAreaTabBar;
#endif
diff --git a/src/widgets/widgets/qmenu.cpp b/src/widgets/widgets/qmenu.cpp
index 4e4c8ebe15..db00f8a650 100644
--- a/src/widgets/widgets/qmenu.cpp
+++ b/src/widgets/widgets/qmenu.cpp
@@ -93,7 +93,9 @@ public:
Q_D(QTornOffMenu);
// make the torn-off menu a sibling of p (instead of a child)
QWidget *parentWidget = d->causedStack.isEmpty() ? p : d->causedStack.constLast();
- if (parentWidget->parentWidget())
+ if (!parentWidget && p)
+ parentWidget = p;
+ if (parentWidget && parentWidget->parentWidget())
parentWidget = parentWidget->parentWidget();
setParent(parentWidget, Qt::Window | Qt::Tool);
setAttribute(Qt::WA_DeleteOnClose, true);
@@ -163,7 +165,7 @@ void QMenuPrivate::init()
defaultMenuAction = menuAction = new QAction(q);
menuAction->setMenu(q); // this calls setOverrideMenuAction
setOverrideMenuAction(nullptr);
- QObject::connect(menuAction, &QAction::changed, [this] {
+ QObject::connect(menuAction, &QAction::changed, q, [this] {
if (!tornPopup.isNull())
tornPopup->updateWindowTitle();
});
@@ -290,6 +292,13 @@ inline bool QMenuPrivate::useFullScreenForPopup() const
QRect QMenuPrivate::popupGeometry(QScreen *screen) const
{
Q_Q(const QMenu);
+ if (screen == nullptr
+#if QT_CONFIG(graphicsview)
+ && q->graphicsProxyWidget() == nullptr
+#endif
+ ) {
+ screen = q->isVisible() ? q->screen() : popupScreen.data();
+ }
if (useFullScreenForPopup())
return screen ? screen->geometry()
: QWidgetPrivate::screenGeometry(q);
@@ -567,10 +576,16 @@ void QMenuPrivate::hideMenu(QMenu *menu)
};
#if QT_CONFIG(effects)
- QSignalBlocker blocker(menu);
+ // If deleteLater has been called and the event loop spins, while waiting
+ // for visual effects to happen, menu might become stale.
+ // To prevent a QSignalBlocker from restoring a stale object, block and restore signals manually.
+ QPointer<QMenu> stillAlive(menu);
+ const bool signalsBlocked = menu->signalsBlocked();
+ menu->blockSignals(true);
+
aboutToHide = true;
// Flash item which is about to trigger (if any).
- if (menu->style()->styleHint(QStyle::SH_Menu_FlashTriggeredItem)
+ if (menu && menu->style()->styleHint(QStyle::SH_Menu_FlashTriggeredItem)
&& currentAction && currentAction == actionAboutToTrigger
&& menu->actions().contains(currentAction)) {
QEventLoop eventLoop;
@@ -581,6 +596,9 @@ void QMenuPrivate::hideMenu(QMenu *menu)
QTimer::singleShot(60, &eventLoop, SLOT(quit()));
eventLoop.exec();
+ if (!stillAlive)
+ return;
+
// Select and wait 20 ms.
menu->setActiveAction(activeAction);
QTimer::singleShot(20, &eventLoop, SLOT(quit()));
@@ -588,10 +606,16 @@ void QMenuPrivate::hideMenu(QMenu *menu)
}
aboutToHide = false;
- blocker.unblock();
+
+ if (stillAlive)
+ menu->blockSignals(signalsBlocked);
+ else
+ return;
+
#endif // QT_CONFIG(effects)
if (activeMenu == menu)
activeMenu = nullptr;
+
menu->d_func()->causedPopup.action = nullptr;
menu->close();
menu->d_func()->causedPopup.widget = nullptr;
@@ -1387,9 +1411,18 @@ bool QMenuPrivate::mouseEventTaken(QMouseEvent *e)
void QMenuPrivate::activateCausedStack(const QList<QPointer<QWidget>> &causedStack, QAction *action,
QAction::ActionEvent action_e, bool self)
{
- QBoolBlocker guard(activationRecursionGuard);
+ Q_Q(QMenu);
+ // can't use QBoolBlocker here
+ const bool activationRecursionGuardReset = activationRecursionGuard;
+ activationRecursionGuard = true;
+ QPointer<QMenu> guard(q);
if (self)
action->activate(action_e);
+ if (!guard)
+ return;
+ auto boolBlocker = qScopeGuard([this, activationRecursionGuardReset]{
+ activationRecursionGuard = activationRecursionGuardReset;
+ });
for(int i = 0; i < causedStack.size(); ++i) {
QPointer<QWidget> widget = causedStack.at(i);
@@ -1465,9 +1498,10 @@ void QMenuPrivate::activateAction(QAction *action, QAction::ActionEvent action_e
#endif
}
-
+ QPointer<QMenu> thisGuard(q);
activateCausedStack(causedStack, action, action_e, self);
-
+ if (!thisGuard)
+ return;
if (action_e == QAction::Hover) {
#if QT_CONFIG(accessibility)
@@ -1698,7 +1732,7 @@ void QMenu::initStyleOption(QStyleOptionMenuItem *option, const QAction *action)
\b{Important inherited functions:} addAction(), removeAction(), clear(),
addSeparator(), and addMenu().
- \sa QMenuBar, {Qt Widgets - Application Example}, {Menus Example}
+ \sa QMenuBar, {Menus Example}
*/
@@ -2282,6 +2316,9 @@ void QMenu::popup(const QPoint &p, QAction *atAction)
void QMenuPrivate::popup(const QPoint &p, QAction *atAction, PositionFunction positionFunction)
{
Q_Q(QMenu);
+ popupScreen = QGuiApplication::screenAt(p);
+ QScopeGuard popupScreenGuard([this](){ popupScreen.clear(); });
+
if (scroll) { // reset scroll state from last popup
if (scroll->scrollOffset)
itemsDirty = 1; // sizeHint will be incorrect if there is previous scroll
@@ -2357,6 +2394,7 @@ void QMenuPrivate::popup(const QPoint &p, QAction *atAction, PositionFunction po
pos = QPushButtonPrivate::get(causedButton)->adjustedMenuPosition();
else
pos = p;
+ popupScreen = QGuiApplication::screenAt(pos);
const QSize menuSizeHint(q->sizeHint());
QSize size = menuSizeHint;
@@ -2495,6 +2533,7 @@ void QMenuPrivate::popup(const QPoint &p, QAction *atAction, PositionFunction po
}
}
}
+ popupScreen = QGuiApplication::screenAt(pos);
q->setGeometry(QRect(pos, size));
#if QT_CONFIG(effects)
int hGuess = q->isRightToLeft() ? QEffects::LeftScroll : QEffects::RightScroll;
@@ -2635,6 +2674,7 @@ QAction *QMenuPrivate::exec(const QPoint &p, QAction *action, PositionFunction p
action = syncAction;
syncAction = nullptr;
eventLoop = nullptr;
+ popupScreen.clear();
return action;
}
@@ -2812,7 +2852,7 @@ void QMenu::paintEvent(QPaintEvent *e)
frame.rect = rect();
frame.palette = palette();
frame.state = QStyle::State_None;
- frame.lineWidth = style()->pixelMetric(QStyle::PM_MenuPanelWidth, &frame);
+ frame.lineWidth = style()->pixelMetric(QStyle::PM_MenuPanelWidth, &frame, this);
frame.midLineWidth = 0;
p.drawPrimitive(QStyle::PE_FrameMenu, frame);
}
diff --git a/src/widgets/widgets/qmenu_p.h b/src/widgets/widgets/qmenu_p.h
index 0d849e5eed..1e32fbcdfa 100644
--- a/src/widgets/widgets/qmenu_p.h
+++ b/src/widgets/widgets/qmenu_p.h
@@ -29,6 +29,8 @@
#include <qpa/qplatformmenu.h>
+#include <QtCore/qpointer.h>
+
#include <functional>
QT_REQUIRE_CONFIG(menu);
@@ -471,6 +473,11 @@ public:
mutable quint8 ncols = 0; // "255cols ought to be enough for anybody."
+ // Contains the screen of the popup point during popup(QPoint).
+ // This is to make sure the screen is remembered,
+ // when the menu contains many items on multiple screens
+ QPointer<QScreen> popupScreen;
+
mutable bool itemsDirty : 1;
mutable bool hasCheckableItems : 1;
bool lastContextMenu : 1;
diff --git a/src/widgets/widgets/qmenubar.cpp b/src/widgets/widgets/qmenubar.cpp
index 18e8807738..c9c9191c17 100644
--- a/src/widgets/widgets/qmenubar.cpp
+++ b/src/widgets/widgets/qmenubar.cpp
@@ -281,17 +281,28 @@ void QMenuBarPrivate::popupAction(QAction *action, bool activateFirst)
if (action->isEnabled() && action->menu()->isEnabled()) {
closePopupMode = 0;
activeMenu = action->menu();
- activeMenu->d_func()->causedPopup.widget = q;
- activeMenu->d_func()->causedPopup.action = action;
+ auto *activeMenuPriv = activeMenu->d_func();
+ activeMenuPriv->causedPopup.widget = q;
+ activeMenuPriv->causedPopup.action = action;
QRect adjustedActionRect = actionRect(action);
- QPoint pos(q->mapToGlobal(QPoint(adjustedActionRect.left(), adjustedActionRect.bottom() + 1)));
- QSize popup_size = activeMenu->sizeHint();
+ QPoint popupPos = adjustedActionRect.bottomLeft() + QPoint(0, 1);
+
//we put the popup menu on the screen containing the bottom-center of the action rect
QScreen *menubarScreen = q->window()->windowHandle()->screen();
- QScreen *popupScreen = menubarScreen->virtualSiblingAt(pos + QPoint(adjustedActionRect.width() / 2, 0));
+ QPoint screenTestPos = q->mapToGlobal(popupPos + QPoint(adjustedActionRect.width() / 2, 0));
+ QPointer<QScreen> popupScreen = menubarScreen->virtualSiblingAt(screenTestPos);
if (!popupScreen)
popupScreen = menubarScreen;
+ std::swap(popupScreen, activeMenuPriv->popupScreen);
+ const QSize popup_size = activeMenu->sizeHint();
+ std::swap(popupScreen, activeMenuPriv->popupScreen);
+
+ // Use screenTestPos.y() for the popup y position. This is the correct global y
+ // consistent with the selected screen in cases where the action rect spans
+ // multiple screens with different scale factors.
+ QPoint pos(q->mapToGlobal(popupPos).x(), screenTestPos.y());
+
QRect screenRect = popupScreen->geometry();
pos = QPoint(qMax(pos.x(), screenRect.x()), qMax(pos.y(), screenRect.y()));
const bool fitUp = (pos.y() - popup_size.height() >= screenRect.top());
@@ -596,11 +607,13 @@ void QMenuBar::initStyleOption(QStyleOptionMenuItem *option, const QAction *acti
Qt for \macos also provides a menu bar merging feature to make
QMenuBar conform more closely to accepted \macos menu bar layout.
- The merging functionality is based on string matching the title of
- a QMenu entry. These strings are translated (using QObject::tr())
- in the "QMenuBar" context. If an entry is moved its slots will still
- fire as if it was in the original place. The table below outlines
- the strings looked for and where the entry is placed if matched:
+ If an entry is moved its slots will still fire as if it was in the
+ original place.
+
+ The merging functionality is based on the QAction::menuRole() of
+ the menu entries. If an item has QAction::TextHeuristicRole,
+ the role is determined by string matching the title using the
+ following heuristics:
\table
\header \li String matches \li Placement \li Notes
@@ -618,8 +631,8 @@ void QMenuBar::initStyleOption(QStyleOptionMenuItem *option, const QAction *acti
created to call QCoreApplication::quit()
\endtable
- You can override this behavior by using the QAction::menuRole()
- property.
+ You can override this behavior by setting the QAction::menuRole()
+ property to QAction::NoRole.
If you want all windows in a Mac application to share one menu
bar, you must create a menu bar that does not have a parent.
@@ -912,7 +925,7 @@ void QMenuBar::paintEvent(QPaintEvent *e)
frame.rect = rect();
frame.palette = palette();
frame.state = QStyle::State_None;
- frame.lineWidth = style()->pixelMetric(QStyle::PM_MenuBarPanelWidth, &frame);
+ frame.lineWidth = style()->pixelMetric(QStyle::PM_MenuBarPanelWidth, &frame, this);
frame.midLineWidth = 0;
style()->drawPrimitive(QStyle::PE_PanelMenuBar, &frame, &p, this);
}
@@ -1285,7 +1298,12 @@ void QMenuBarPrivate::handleReparent()
QList<QPointer<QWidget>> newParents;
// Remove event filters on ex-parents, keep them on still-parents
// The parents are always ordered in the vector
- foreach (const QPointer<QWidget> &w, oldParents) {
+ //
+ // Take a copy because this method is called from changeEvent() and eventFilter(),
+ // which might cause recursion into the class due to event processing, which might
+ // modify oldParents.
+ const auto copy = oldParents;
+ for (const QPointer<QWidget> &w : copy) {
if (w) {
if (newParent == w) {
newParents.append(w);
@@ -1772,6 +1790,7 @@ void QMenuBar::setNativeMenuBar(bool nativeMenuBar)
if (!nativeMenuBar) {
delete d->platformMenuBar;
d->platformMenuBar = nullptr;
+ d->itemsDirty = true;
} else {
if (!d->platformMenuBar)
d->platformMenuBar = QGuiApplicationPrivate::platformTheme()->createPlatformMenuBar();
diff --git a/src/widgets/widgets/qmenubar_p.h b/src/widgets/widgets/qmenubar_p.h
index ee524c3acb..549a4c7a1f 100644
--- a/src/widgets/widgets/qmenubar_p.h
+++ b/src/widgets/widgets/qmenubar_p.h
@@ -20,6 +20,8 @@
#include <private/qmenu_p.h> // Mac needs what in this file!
#include <qpa/qplatformmenu.h>
+#include <QtCore/qpointer.h>
+
QT_REQUIRE_CONFIG(menubar);
QT_BEGIN_NAMESPACE
diff --git a/src/widgets/widgets/qplaintextedit.cpp b/src/widgets/widgets/qplaintextedit.cpp
index cd9889e9ff..8362c6c629 100644
--- a/src/widgets/widgets/qplaintextedit.cpp
+++ b/src/widgets/widgets/qplaintextedit.cpp
@@ -418,7 +418,7 @@ QPlainTextEditControl::QPlainTextEditControl(QPlainTextEdit *parent)
setAcceptRichText(false);
}
-void QPlainTextEditPrivate::_q_cursorPositionChanged()
+void QPlainTextEditPrivate::cursorPositionChanged()
{
pageUpDownLastCursorYIsValid = false;
Q_Q(QPlainTextEdit);
@@ -429,11 +429,18 @@ void QPlainTextEditPrivate::_q_cursorPositionChanged()
emit q->cursorPositionChanged();
}
-void QPlainTextEditPrivate::_q_verticalScrollbarActionTriggered(int action) {
- if (action == QAbstractSlider::SliderPageStepAdd) {
+void QPlainTextEditPrivate::verticalScrollbarActionTriggered(int action) {
+
+ const auto a = static_cast<QAbstractSlider::SliderAction>(action);
+ switch (a) {
+ case QAbstractSlider::SliderPageStepAdd:
pageUpDown(QTextCursor::Down, QTextCursor::MoveAnchor, false);
- } else if (action == QAbstractSlider::SliderPageStepSub) {
+ break;
+ case QAbstractSlider::SliderPageStepSub:
pageUpDown(QTextCursor::Up, QTextCursor::MoveAnchor, false);
+ break;
+ default:
+ break;
}
}
@@ -622,10 +629,7 @@ void QPlainTextEditPrivate::setTopBlock(int blockNumber, int lineNumber, int dx)
lineNumber = maxTopLine - block.firstLineNumber();
}
- {
- const QSignalBlocker blocker(vbar);
- vbar->setValue(newTopLine);
- }
+ vbar->setValue(newTopLine);
if (!dx && blockNumber == control->topBlock && lineNumber == topLine)
return;
@@ -641,10 +645,7 @@ void QPlainTextEditPrivate::setTopBlock(int blockNumber, int lineNumber, int dx)
control->topBlock = blockNumber;
topLine = lineNumber;
- {
- const QSignalBlocker blocker(vbar);
- vbar->setValue(block.firstLineNumber() + lineNumber);
- }
+ vbar->setValue(block.firstLineNumber() + lineNumber);
if (dx || dy) {
viewport->scroll(q->isRightToLeft() ? -dx : dx, dy);
@@ -724,9 +725,14 @@ void QPlainTextEditPrivate::updateViewport()
}
QPlainTextEditPrivate::QPlainTextEditPrivate()
- : tabChangesFocus(false), showCursorOnInitialShow(false), backgroundVisible(false),
- centerOnScroll(false), inDrag(false), clickCausedFocus(false), placeholderVisible(true),
- pageUpDownLastCursorYIsValid(false)
+ : tabChangesFocus(false)
+ , showCursorOnInitialShow(false)
+ , backgroundVisible(false)
+ , centerOnScroll(false)
+ , inDrag(false)
+ , clickCausedFocus(false)
+ , pageUpDownLastCursorYIsValid(false)
+ , placeholderTextShown(false)
{
}
@@ -742,23 +748,28 @@ void QPlainTextEditPrivate::init(const QString &txt)
control->setPalette(q->palette());
- QObject::connect(vbar, SIGNAL(actionTriggered(int)), q, SLOT(_q_verticalScrollbarActionTriggered(int)));
-
- QObject::connect(control, SIGNAL(microFocusChanged()), q, SLOT(updateMicroFocus()));
- QObject::connect(control, SIGNAL(documentSizeChanged(QSizeF)), q, SLOT(_q_adjustScrollbars()));
- QObject::connect(control, SIGNAL(blockCountChanged(int)), q, SIGNAL(blockCountChanged(int)));
- QObject::connect(control, SIGNAL(updateRequest(QRectF)), q, SLOT(_q_repaintContents(QRectF)));
- QObject::connect(control, SIGNAL(modificationChanged(bool)), q, SIGNAL(modificationChanged(bool)));
-
- QObject::connect(control, SIGNAL(textChanged()), q, SIGNAL(textChanged()));
- QObject::connect(control, SIGNAL(undoAvailable(bool)), q, SIGNAL(undoAvailable(bool)));
- QObject::connect(control, SIGNAL(redoAvailable(bool)), q, SIGNAL(redoAvailable(bool)));
- QObject::connect(control, SIGNAL(copyAvailable(bool)), q, SIGNAL(copyAvailable(bool)));
- QObject::connect(control, SIGNAL(selectionChanged()), q, SIGNAL(selectionChanged()));
- QObject::connect(control, SIGNAL(cursorPositionChanged()), q, SLOT(_q_cursorPositionChanged()));
-
- QObject::connect(control, SIGNAL(textChanged()), q, SLOT(_q_updatePlaceholderVisibility()));
- QObject::connect(control, SIGNAL(textChanged()), q, SLOT(updateMicroFocus()));
+ QObjectPrivate::connect(vbar, &QAbstractSlider::actionTriggered,
+ this, &QPlainTextEditPrivate::verticalScrollbarActionTriggered);
+ QObject::connect(control, &QWidgetTextControl::microFocusChanged, q,
+ [q](){q->updateMicroFocus(); });
+ QObjectPrivate::connect(control, &QWidgetTextControl::documentSizeChanged,
+ this, &QPlainTextEditPrivate::adjustScrollbars);
+ QObject::connect(control, &QWidgetTextControl::blockCountChanged,
+ q, &QPlainTextEdit::blockCountChanged);
+ QObjectPrivate::connect(control, &QWidgetTextControl::updateRequest,
+ this, &QPlainTextEditPrivate::repaintContents);
+ QObject::connect(control, &QWidgetTextControl::modificationChanged,
+ q, &QPlainTextEdit::modificationChanged);
+ QObject::connect(control, &QWidgetTextControl::textChanged, q, &QPlainTextEdit::textChanged);
+ QObject::connect(control, &QWidgetTextControl::undoAvailable, q, &QPlainTextEdit::undoAvailable);
+ QObject::connect(control, &QWidgetTextControl::redoAvailable, q, &QPlainTextEdit::redoAvailable);
+ QObject::connect(control, &QWidgetTextControl::copyAvailable, q, &QPlainTextEdit::copyAvailable);
+ QObject::connect(control, &QWidgetTextControl::selectionChanged, q, &QPlainTextEdit::selectionChanged);
+ QObjectPrivate::connect(control, &QWidgetTextControl::cursorPositionChanged,
+ this, &QPlainTextEditPrivate::cursorPositionChanged);
+ QObjectPrivate::connect(control, &QWidgetTextControl::textChanged,
+ this, &QPlainTextEditPrivate::updatePlaceholderVisibility);
+ QObject::connect(control, &QWidgetTextControl::textChanged, q, [q](){q->updateMicroFocus(); });
// set a null page size initially to avoid any relayouting until the textedit
// is shown. relayoutDocument() will take care of setting the page size to the
@@ -786,26 +797,19 @@ void QPlainTextEditPrivate::init(const QString &txt)
#endif
}
-void QPlainTextEditPrivate::_q_updatePlaceholderVisibility()
+void QPlainTextEditPrivate::updatePlaceholderVisibility()
{
- Q_Q(QPlainTextEdit);
-
// We normally only repaint the part of view that contains text in the
- // document that has changed (in _q_repaintContents). But the placeholder
+ // document that has changed (in repaintContents). But the placeholder
// text is not a part of the document, but is drawn on separately. So whenever
// we either show or hide the placeholder text, we issue a full update.
- bool placeholderCurrentyVisible = placeholderVisible;
-
- placeholderVisible = !placeholderText.isEmpty()
- && q->document()->isEmpty()
- && (!q->firstVisibleBlock().isValid() ||
- q->firstVisibleBlock().layout()->preeditAreaText().isEmpty());
-
- if (placeholderCurrentyVisible != placeholderVisible)
+ if (placeholderTextShown != placeHolderTextToBeShown()) {
viewport->update();
+ placeholderTextShown = placeHolderTextToBeShown();
+ }
}
-void QPlainTextEditPrivate::_q_repaintContents(const QRectF &contentsRect)
+void QPlainTextEditPrivate::repaintContents(const QRectF &contentsRect)
{
Q_Q(QPlainTextEdit);
if (!contentsRect.isValid()) {
@@ -938,7 +942,7 @@ void QPlainTextEditPrivate::pageUpDown(QTextCursor::MoveOperation op, QTextCurso
#if QT_CONFIG(scrollbar)
-void QPlainTextEditPrivate::_q_adjustScrollbars()
+void QPlainTextEditPrivate::adjustScrollbars()
{
Q_Q(QPlainTextEdit);
QTextDocument *doc = control->document();
@@ -992,8 +996,6 @@ void QPlainTextEditPrivate::_q_adjustScrollbars()
vSliderLength = lineSpacing != 0 ? viewport->height() / lineSpacing : 0;
}
-
-
QSizeF documentSize = documentLayout->documentSize();
vbar->setRange(0, qMax(0, vmax));
vbar->setPageStep(vSliderLength);
@@ -1002,10 +1004,7 @@ void QPlainTextEditPrivate::_q_adjustScrollbars()
if (firstVisibleBlock.isValid())
visualTopLine = firstVisibleBlock.firstLineNumber() + topLine;
- {
- const QSignalBlocker blocker(vbar);
- vbar->setValue(visualTopLine);
- }
+ vbar->setValue(visualTopLine);
hbar->setRange(0, (int)documentSize.width() - viewport->width());
hbar->setPageStep(viewport->width());
@@ -1185,10 +1184,8 @@ void QPlainTextEditPrivate::ensureViewportLayouted()
editor with line wrap enabled in real time. It also makes for a
fast log viewer (see setMaximumBlockCount()).
-
- \sa QTextDocument, QTextCursor, {Qt Widgets - Application Example},
- {Code Editor Example}, {Syntax Highlighter Example},
- {Rich Text Processing}
+ \sa QTextDocument, QTextCursor
+ {Syntax Highlighter Example}, {Rich Text Processing}
*/
@@ -1300,7 +1297,7 @@ void QPlainTextEdit::setDocument(QTextDocument *document)
d->documentLayoutPtr = documentLayout;
d->updateDefaultTextOption();
d->relayoutDocument();
- d->_q_adjustScrollbars();
+ d->adjustScrollbars();
}
/*!
@@ -1332,7 +1329,7 @@ void QPlainTextEdit::setPlaceholderText(const QString &placeholderText)
Q_D(QPlainTextEdit);
if (d->placeholderText != placeholderText) {
d->placeholderText = placeholderText;
- d->_q_updatePlaceholderVisibility();
+ d->updatePlaceholderVisibility();
}
}
@@ -1512,53 +1509,61 @@ bool QPlainTextEdit::event(QEvent *e)
{
Q_D(QPlainTextEdit);
+ switch (e->type()) {
#ifndef QT_NO_CONTEXTMENU
- if (e->type() == QEvent::ContextMenu
- && static_cast<QContextMenuEvent *>(e)->reason() == QContextMenuEvent::Keyboard) {
- ensureCursorVisible();
- const QPoint cursorPos = cursorRect().center();
- QContextMenuEvent ce(QContextMenuEvent::Keyboard, cursorPos, d->viewport->mapToGlobal(cursorPos));
- ce.setAccepted(e->isAccepted());
- const bool result = QAbstractScrollArea::event(&ce);
- e->setAccepted(ce.isAccepted());
- return result;
- }
+ case QEvent::ContextMenu:
+ if (static_cast<QContextMenuEvent *>(e)->reason() == QContextMenuEvent::Keyboard) {
+ ensureCursorVisible();
+ const QPoint cursorPos = cursorRect().center();
+ QContextMenuEvent ce(QContextMenuEvent::Keyboard, cursorPos, d->viewport->mapToGlobal(cursorPos));
+ ce.setAccepted(e->isAccepted());
+ const bool result = QAbstractScrollArea::event(&ce);
+ e->setAccepted(ce.isAccepted());
+ return result;
+ }
+ break;
#endif // QT_NO_CONTEXTMENU
- if (e->type() == QEvent::ShortcutOverride
- || e->type() == QEvent::ToolTip) {
+ case QEvent::ShortcutOverride:
+ case QEvent::ToolTip:
d->sendControlEvent(e);
- }
+ break;
#ifdef QT_KEYPAD_NAVIGATION
- else if (e->type() == QEvent::EnterEditFocus || e->type() == QEvent::LeaveEditFocus) {
+ case QEvent::EnterEditFocus:
+ case QEvent::LeaveEditFocus:
if (QApplicationPrivate::keypadNavigationEnabled())
d->sendControlEvent(e);
- }
+ break;
#endif
#ifndef QT_NO_GESTURES
- else if (e->type() == QEvent::Gesture) {
- QGestureEvent *ge = static_cast<QGestureEvent *>(e);
- QPanGesture *g = static_cast<QPanGesture *>(ge->gesture(Qt::PanGesture));
- if (g) {
+ case QEvent::Gesture:
+ if (auto *g = static_cast<QGestureEvent *>(e)->gesture(Qt::PanGesture)) {
+ QPanGesture *panGesture = static_cast<QPanGesture *>(g);
QScrollBar *hBar = horizontalScrollBar();
QScrollBar *vBar = verticalScrollBar();
- if (g->state() == Qt::GestureStarted)
+ if (panGesture->state() == Qt::GestureStarted)
d->originalOffsetY = vBar->value();
- QPointF offset = g->offset();
+ QPointF offset = panGesture->offset();
if (!offset.isNull()) {
if (QGuiApplication::isRightToLeft())
offset.rx() *= -1;
// QPlainTextEdit scrolls by lines only in vertical direction
QFontMetrics fm(document()->defaultFont());
int lineHeight = fm.height();
- int newX = hBar->value() - g->delta().x();
+ int newX = hBar->value() - panGesture->delta().x();
int newY = d->originalOffsetY - offset.y()/lineHeight;
hBar->setValue(newX);
vBar->setValue(newY);
}
}
return true;
- }
#endif // QT_NO_GESTURES
+ case QEvent::WindowActivate:
+ case QEvent::WindowDeactivate:
+ d->control->setPalette(palette());
+ break;
+ default:
+ break;
+ }
return QAbstractScrollArea::event(e);
}
@@ -1841,7 +1846,7 @@ void QPlainTextEdit::resizeEvent(QResizeEvent *e)
Q_D(QPlainTextEdit);
if (e->oldSize().width() != e->size().width())
d->relayoutDocument();
- d->_q_adjustScrollbars();
+ d->adjustScrollbars();
}
void QPlainTextEditPrivate::relayoutDocument()
@@ -1907,7 +1912,7 @@ void QPlainTextEdit::paintEvent(QPaintEvent *e)
er.setRight(qMin(er.right(), maxX));
painter.setClipRect(er);
- if (d->placeholderVisible) {
+ if (d->placeHolderTextToBeShown()) {
const QColor col = d->control->palette().placeholderText().color();
painter.setPen(col);
painter.setClipRect(e->rect());
@@ -2207,7 +2212,7 @@ QVariant QPlainTextEdit::inputMethodQuery(Qt::InputMethodQuery query, QVariant a
Q_D(const QPlainTextEdit);
switch (query) {
case Qt::ImEnabled:
- return isEnabled();
+ return isEnabled() && !isReadOnly();
case Qt::ImHints:
case Qt::ImInputItemClipRectangle:
return QWidget::inputMethodQuery(query);
@@ -2281,7 +2286,7 @@ void QPlainTextEdit::showEvent(QShowEvent *)
d->showCursorOnInitialShow = false;
ensureCursorVisible();
}
- d->_q_adjustScrollbars();
+ d->adjustScrollbars();
}
/*! \reimp
@@ -2290,20 +2295,29 @@ void QPlainTextEdit::changeEvent(QEvent *e)
{
Q_D(QPlainTextEdit);
QAbstractScrollArea::changeEvent(e);
- if (e->type() == QEvent::ApplicationFontChange
- || e->type() == QEvent::FontChange) {
+
+ switch (e->type()) {
+ case QEvent::ApplicationFontChange:
+ case QEvent::FontChange:
d->control->document()->setDefaultFont(font());
- } else if (e->type() == QEvent::ActivationChange) {
+ break;
+ case QEvent::ActivationChange:
if (!isActiveWindow())
d->autoScrollTimer.stop();
- } else if (e->type() == QEvent::EnabledChange) {
+ break;
+ case QEvent::EnabledChange:
e->setAccepted(isEnabled());
d->control->setPalette(palette());
d->sendControlEvent(e);
- } else if (e->type() == QEvent::PaletteChange) {
+ break;
+ case QEvent::PaletteChange:
d->control->setPalette(palette());
- } else if (e->type() == QEvent::LayoutDirectionChange) {
+ break;
+ case QEvent::LayoutDirectionChange:
d->sendControlEvent(e);
+ break;
+ default:
+ break;
}
}
@@ -2589,7 +2603,7 @@ void QPlainTextEdit::insertFromMimeData(const QMimeData *source)
bool QPlainTextEdit::isReadOnly() const
{
Q_D(const QPlainTextEdit);
- return !(d->control->textInteractionFlags() & Qt::TextEditable);
+ return !d->control || !(d->control->textInteractionFlags() & Qt::TextEditable);
}
void QPlainTextEdit::setReadOnly(bool ro)
@@ -2774,7 +2788,7 @@ void QPlainTextEdit::setLineWrapMode(LineWrapMode wrap)
d->lineWrap = wrap;
d->updateDefaultTextOption();
d->relayoutDocument();
- d->_q_adjustScrollbars();
+ d->adjustScrollbars();
ensureCursorVisible();
}
@@ -2860,7 +2874,7 @@ void QPlainTextEdit::setCenterOnScroll(bool enabled)
if (enabled == d->centerOnScroll)
return;
d->centerOnScroll = enabled;
- d->_q_adjustScrollbars();
+ d->adjustScrollbars();
}
@@ -2883,11 +2897,14 @@ bool QPlainTextEdit::find(const QString &exp, QTextDocument::FindFlags options)
\overload
Finds the next occurrence, matching the regular expression, \a exp, using the given
- \a options. The QTextDocument::FindCaseSensitively option is ignored for this overload,
- use QRegularExpression::CaseInsensitiveOption instead.
+ \a options.
Returns \c true if a match was found and changes the cursor to select the match;
otherwise returns \c false.
+
+ \warning For historical reasons, the case sensitivity option set on
+ \a exp is ignored. Instead, the \a options are used to determine
+ if the search is case sensitive or not.
*/
#if QT_CONFIG(regularexpression)
bool QPlainTextEdit::find(const QRegularExpression &exp, QTextDocument::FindFlags options)
@@ -2988,12 +3005,17 @@ void QPlainTextEditPrivate::append(const QString &text, Qt::TextFormat format)
bool documentSizeChangedBlocked = documentLayout->priv()->blockDocumentSizeChanged;
documentLayout->priv()->blockDocumentSizeChanged = true;
- if (format == Qt::RichText)
+ switch (format) {
+ case Qt::RichText:
control->appendHtml(text);
- else if (format == Qt::PlainText)
+ break;
+ case Qt::PlainText:
control->appendPlainText(text);
- else
+ break;
+ default:
control->append(text);
+ break;
+ }
if (maximumBlockCount > 0) {
if (document->blockCount() > maximumBlockCount) {
@@ -3015,7 +3037,7 @@ void QPlainTextEditPrivate::append(const QString &text, Qt::TextFormat format)
}
documentLayout->priv()->blockDocumentSizeChanged = documentSizeChangedBlocked;
- _q_adjustScrollbars();
+ adjustScrollbars();
if (atBottom) {
diff --git a/src/widgets/widgets/qplaintextedit.h b/src/widgets/widgets/qplaintextedit.h
index d42b67fa69..89a4d3ab04 100644
--- a/src/widgets/widgets/qplaintextedit.h
+++ b/src/widgets/widgets/qplaintextedit.h
@@ -240,11 +240,6 @@ protected:
private:
Q_DISABLE_COPY(QPlainTextEdit)
- Q_PRIVATE_SLOT(d_func(), void _q_repaintContents(const QRectF &r))
- Q_PRIVATE_SLOT(d_func(), void _q_updatePlaceholderVisibility())
- Q_PRIVATE_SLOT(d_func(), void _q_adjustScrollbars())
- Q_PRIVATE_SLOT(d_func(), void _q_verticalScrollbarActionTriggered(int))
- Q_PRIVATE_SLOT(d_func(), void _q_cursorPositionChanged())
friend class QPlainTextEditControl;
};
diff --git a/src/widgets/widgets/qplaintextedit_p.h b/src/widgets/widgets/qplaintextedit_p.h
index 541011cc7e..9ca1cdcc67 100644
--- a/src/widgets/widgets/qplaintextedit_p.h
+++ b/src/widgets/widgets/qplaintextedit_p.h
@@ -32,6 +32,8 @@
#include "private/qwidgettextcontrol_p.h"
+#include <QtCore/qpointer.h>
+
QT_REQUIRE_CONFIG(textedit);
QT_BEGIN_NAMESPACE
@@ -84,14 +86,14 @@ public:
QPlainTextEditPrivate();
void init(const QString &txt = QString());
- void _q_repaintContents(const QRectF &contentsRect);
- void _q_updatePlaceholderVisibility();
+ void repaintContents(const QRectF &contentsRect);
+ void updatePlaceholderVisibility();
inline QPoint mapToContents(const QPoint &point) const
{ return QPoint(point.x() + horizontalOffset(), point.y() + verticalOffset()); }
- void _q_adjustScrollbars();
- void _q_verticalScrollbarActionTriggered(int action);
+ void adjustScrollbars();
+ void verticalScrollbarActionTriggered(int action);
void ensureViewportLayouted();
void relayoutDocument();
@@ -128,8 +130,8 @@ public:
uint centerOnScroll : 1;
uint inDrag : 1;
uint clickCausedFocus : 1;
- uint placeholderVisible : 1;
uint pageUpDownLastCursorYIsValid : 1;
+ uint placeholderTextShown : 1;
void setTopLine(int visualTopLine, int dx = 0);
void setTopBlock(int newTopBlock, int newTopLine, int dx = 0);
@@ -142,8 +144,13 @@ public:
void append(const QString &text, Qt::TextFormat format = Qt::AutoText);
- void _q_cursorPositionChanged();
- void _q_modificationChanged(bool);
+ void cursorPositionChanged();
+ void modificationChanged(bool);
+ inline bool placeHolderTextToBeShown() const
+ {
+ Q_Q(const QPlainTextEdit);
+ return q->document()->isEmpty() && !q->placeholderText().isEmpty();
+ }
};
QT_END_NAMESPACE
diff --git a/src/widgets/widgets/qpushbutton.cpp b/src/widgets/widgets/qpushbutton.cpp
index ec882db175..c180a08d62 100644
--- a/src/widgets/widgets/qpushbutton.cpp
+++ b/src/widgets/widgets/qpushbutton.cpp
@@ -525,7 +525,8 @@ void QPushButton::setMenu(QMenu* menu)
return;
if (menu && !d->menu) {
- connect(this, SIGNAL(pressed()), this, SLOT(_q_popupPressed()), Qt::UniqueConnection);
+ QObjectPrivate::connect(this, &QPushButton::pressed,
+ d, &QPushButtonPrivate::popupPressed, Qt::UniqueConnection);
}
if (d->menu)
removeAction(d->menu->menuAction());
@@ -562,10 +563,10 @@ void QPushButton::showMenu()
if (!d || !d->menu)
return;
setDown(true);
- d->_q_popupPressed();
+ d->popupPressed();
}
-void QPushButtonPrivate::_q_popupPressed()
+void QPushButtonPrivate::popupPressed()
{
Q_Q(QPushButton);
if (!down || !menu)
@@ -575,17 +576,14 @@ void QPushButtonPrivate::_q_popupPressed()
QPoint menuPos = adjustedMenuPosition();
- QPointer<QPushButton> guard(q);
- QMenuPrivate::get(menu)->causedPopup.widget = guard;
+ QMenuPrivate::get(menu)->causedPopup.widget = q;
//Because of a delay in menu effects, we must keep track of the
//menu visibility to avoid flicker on button release
menuOpen = true;
- menu->exec(menuPos);
- if (guard) {
- menuOpen = false;
- q->setDown(false);
- }
+ QObject::connect(menu, &QMenu::aboutToHide,
+ q, [q, this]{ menuOpen = false; q->setDown(false); }, Qt::SingleShotConnection);
+ menu->popup(menuPos);
}
QPoint QPushButtonPrivate::adjustedMenuPosition()
diff --git a/src/widgets/widgets/qpushbutton.h b/src/widgets/widgets/qpushbutton.h
index 6162d6dc80..3c420b5313 100644
--- a/src/widgets/widgets/qpushbutton.h
+++ b/src/widgets/widgets/qpushbutton.h
@@ -62,14 +62,9 @@ protected:
bool hitButton(const QPoint &pos) const override;
QPushButton(QPushButtonPrivate &dd, QWidget* parent = nullptr);
-public:
-
private:
Q_DISABLE_COPY(QPushButton)
Q_DECLARE_PRIVATE(QPushButton)
-#if QT_CONFIG(menu)
- Q_PRIVATE_SLOT(d_func(), void _q_popupPressed())
-#endif
};
QT_END_NAMESPACE
diff --git a/src/widgets/widgets/qpushbutton_p.h b/src/widgets/widgets/qpushbutton_p.h
index 4c97663343..0817589baf 100644
--- a/src/widgets/widgets/qpushbutton_p.h
+++ b/src/widgets/widgets/qpushbutton_p.h
@@ -7,6 +7,8 @@
#include <QtWidgets/private/qtwidgetsglobal_p.h>
#include "private/qabstractbutton_p.h"
+#include <QtCore/qpointer.h>
+
QT_REQUIRE_CONFIG(pushbutton);
//
@@ -43,7 +45,7 @@ public:
QPoint adjustedMenuPosition();
#endif
void resetLayoutItemMargins();
- void _q_popupPressed();
+ void popupPressed();
#if QT_CONFIG(dialog)
QDialog *dialogParent() const;
#else
diff --git a/src/widgets/widgets/qradiobutton.cpp b/src/widgets/widgets/qradiobutton.cpp
index e34b04e5ee..af1bd3f649 100644
--- a/src/widgets/widgets/qradiobutton.cpp
+++ b/src/widgets/widgets/qradiobutton.cpp
@@ -82,7 +82,7 @@ void QRadioButtonPrivate::init()
setDown(), isDown(), autoRepeat(), group(), setAutoRepeat(),
toggle(), pressed(), released(), clicked(), and toggled().
- \sa QPushButton, QToolButton, QCheckBox, {Group Box Example}
+ \sa QPushButton, QToolButton, QCheckBox
*/
diff --git a/src/widgets/widgets/qscrollarea.cpp b/src/widgets/widgets/qscrollarea.cpp
index 00e2f42a81..efbf4da07f 100644
--- a/src/widgets/widgets/qscrollarea.cpp
+++ b/src/widgets/widgets/qscrollarea.cpp
@@ -91,11 +91,7 @@ QT_BEGIN_NAMESPACE
cause the size of the scroll area to be updated whenever the
contents of the layout changes.
- For a complete example using the QScrollArea class, see the \l
- {widgets/imageviewer}{Image Viewer} example. The example shows how
- to combine QLabel and QScrollArea to display an image.
-
- \sa QAbstractScrollArea, QScrollBar, {Image Viewer Example}
+ \sa QAbstractScrollArea, QScrollBar
*/
diff --git a/src/widgets/widgets/qscrollarea_p.h b/src/widgets/widgets/qscrollarea_p.h
index 8d066e9e17..a599fa8c36 100644
--- a/src/widgets/widgets/qscrollarea_p.h
+++ b/src/widgets/widgets/qscrollarea_p.h
@@ -20,6 +20,8 @@
#include "private/qabstractscrollarea_p.h"
#include <QtWidgets/qscrollbar.h>
+#include <QtCore/qpointer.h>
+
QT_REQUIRE_CONFIG(scrollarea);
QT_BEGIN_NAMESPACE
diff --git a/src/widgets/widgets/qscrollbar.cpp b/src/widgets/widgets/qscrollbar.cpp
index ed456c56b8..789cffba99 100644
--- a/src/widgets/widgets/qscrollbar.cpp
+++ b/src/widgets/widgets/qscrollbar.cpp
@@ -12,7 +12,10 @@
#if QT_CONFIG(menu)
#include "qmenu.h"
#endif
+
#include <QtCore/qelapsedtimer.h>
+#include <QtCore/qpointer.h>
+
#if QT_CONFIG(accessibility)
#include "qaccessible.h"
@@ -189,7 +192,9 @@ void QScrollBarPrivate::setTransient(bool value)
if (transient != value) {
transient = value;
if (q->isVisible()) {
- if (q->style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, q))
+ QStyleOptionSlider opt;
+ q->initStyleOption(&opt);
+ if (q->style()->styleHint(QStyle::SH_ScrollBar_Transient, &opt, q))
q->update();
} else if (!transient) {
q->show();
@@ -200,7 +205,9 @@ void QScrollBarPrivate::setTransient(bool value)
void QScrollBarPrivate::flash()
{
Q_Q(QScrollBar);
- if (!flashed && q->style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, q)) {
+ QStyleOptionSlider opt;
+ q->initStyleOption(&opt);
+ if (!flashed && q->style()->styleHint(QStyle::SH_ScrollBar_Transient, &opt, q)) {
flashed = true;
if (!q->isVisible())
q->show();
@@ -284,7 +291,7 @@ void QScrollBar::initStyleOption(QStyleOptionSlider *option) const
option->upsideDown = d->invertedAppearance;
if (d->orientation == Qt::Horizontal)
option->state |= QStyle::State_Horizontal;
- if ((d->flashed || !d->transient) && style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, this))
+ if ((d->flashed || !d->transient) && style()->styleHint(QStyle::SH_ScrollBar_Transient, option, this))
option->state |= QStyle::State_On;
}
@@ -341,7 +348,9 @@ void QScrollBarPrivate::init()
invertedControls = true;
pressedControl = hoverControl = QStyle::SC_None;
pointerOutsidePressedControl = false;
- transient = q->style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, q);
+ QStyleOption opt;
+ opt.initFrom(q);
+ transient = q->style()->styleHint(QStyle::SH_ScrollBar_Transient, &opt, q);
flashed = false;
flashTimer = 0;
q->setFocusPolicy(Qt::NoFocus);
@@ -435,12 +444,17 @@ bool QScrollBar::event(QEvent *event)
if (const QHoverEvent *he = static_cast<const QHoverEvent *>(event))
d_func()->updateHoverControl(he->position().toPoint());
break;
- case QEvent::StyleChange:
- d_func()->setTransient(style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, this));
+ case QEvent::StyleChange: {
+ QStyleOptionSlider opt;
+ initStyleOption(&opt);
+ d_func()->setTransient(style()->styleHint(QStyle::SH_ScrollBar_Transient, &opt, this));
break;
+ }
case QEvent::Timer:
if (static_cast<QTimerEvent *>(event)->timerId() == d->flashTimer) {
- if (d->flashed && style()->styleHint(QStyle::SH_ScrollBar_Transient, nullptr, this)) {
+ QStyleOptionSlider opt;
+ initStyleOption(&opt);
+ if (d->flashed && style()->styleHint(QStyle::SH_ScrollBar_Transient, &opt, this)) {
d->flashed = false;
update();
}
diff --git a/src/widgets/widgets/qsizegrip.cpp b/src/widgets/widgets/qsizegrip.cpp
index 8ffcb0081b..2500983450 100644
--- a/src/widgets/widgets/qsizegrip.cpp
+++ b/src/widgets/widgets/qsizegrip.cpp
@@ -18,6 +18,8 @@
#include <qpa/qplatformtheme.h>
#include <QtWidgets/qabstractscrollarea.h>
+#include <QtCore/qpointer.h>
+
QT_BEGIN_NAMESPACE
static QWidget *qt_sizegrip_topLevelWidget(QWidget* w)
@@ -73,7 +75,7 @@ public:
void _q_showIfNotHidden()
{
Q_Q(QSizeGrip);
- bool showSizeGrip = !(q->isHidden() && q->testAttribute(Qt::WA_WState_ExplicitShowHide));
+ bool showSizeGrip = !isExplicitlyHidden();
updateTopLevelWidget();
if (tlw && showSizeGrip) {
Qt::WindowStates sizeGripNotVisibleState = Qt::WindowFullScreen;
@@ -449,7 +451,7 @@ void QSizeGrip::setVisible(bool visible)
bool QSizeGrip::eventFilter(QObject *o, QEvent *e)
{
Q_D(QSizeGrip);
- if ((isHidden() && testAttribute(Qt::WA_WState_ExplicitShowHide))
+ if (d->isExplicitlyHidden()
|| e->type() != QEvent::WindowStateChange
|| o != d->tlw) {
return QWidget::eventFilter(o, e);
diff --git a/src/widgets/widgets/qspinbox.cpp b/src/widgets/widgets/qspinbox.cpp
index 5178763bc1..73946d54a9 100644
--- a/src/widgets/widgets/qspinbox.cpp
+++ b/src/widgets/widgets/qspinbox.cpp
@@ -132,12 +132,9 @@ public:
for a custom spin box that allows the user to enter icon sizes
(e.g., "32 x 32"):
- \snippet widgets/icons/iconsizespinbox.cpp 1
+ \snippet code/src_gui_widgets_qspinbox.cpp 8
\codeline
- \snippet widgets/icons/iconsizespinbox.cpp 2
-
- See the \l{widgets/icons}{Icons} example for the full source
- code.
+ \snippet code/src_gui_widgets_qspinbox.cpp 9
\sa QDoubleSpinBox, QDateTimeEdit, QSlider, {Spin Boxes Example}
*/
diff --git a/src/widgets/widgets/qsplashscreen.cpp b/src/widgets/widgets/qsplashscreen.cpp
index 040b7424cf..fcd09908cd 100644
--- a/src/widgets/widgets/qsplashscreen.cpp
+++ b/src/widgets/widgets/qsplashscreen.cpp
@@ -32,6 +32,8 @@ public:
int currAlign;
inline QSplashScreenPrivate();
+
+ void handlePaintEvent();
};
/*!
@@ -67,9 +69,9 @@ public:
\snippet qsplashscreen/main.cpp 1
The user can hide the splash screen by clicking on it with the
- mouse. Since the splash screen is typically displayed before the
- event loop has started running, it is necessary to periodically
- call QCoreApplication::processEvents() to receive the mouse clicks.
+ mouse. For mouse handling to work, call QApplication::processEvents()
+ periodically during startup.
+
It is sometimes useful to update the splash screen with messages,
for example, announcing connections established or modules loaded
@@ -201,18 +203,21 @@ void QSplashScreen::clearMessage()
repaint();
}
-// A copy of Qt Test's qWaitForWindowExposed() and qSleep().
-inline static bool waitForWindowExposed(QWindow *window, int timeout = 1000)
+static bool waitForWidgetMapped(QWidget *widget, int timeout = 1000)
{
enum { TimeOutMs = 10 };
+ auto isMapped = [widget](){
+ return widget->windowHandle() && widget->windowHandle()->isVisible();
+ };
+
QElapsedTimer timer;
timer.start();
- while (!window->isExposed()) {
+ while (!isMapped()) {
const int remaining = timeout - int(timer.elapsed());
if (remaining <= 0)
break;
QCoreApplication::processEvents(QEventLoop::AllEvents, remaining);
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
+ QCoreApplication::sendPostedEvents();
#if defined(Q_OS_WIN)
Sleep(uint(TimeOutMs));
#else
@@ -220,7 +225,7 @@ inline static bool waitForWindowExposed(QWindow *window, int timeout = 1000)
nanosleep(&ts, nullptr);
#endif
}
- return window->isExposed();
+ return isMapped();
}
/*!
@@ -233,7 +238,7 @@ void QSplashScreen::finish(QWidget *mainWin)
if (mainWin) {
if (!mainWin->windowHandle())
mainWin->createWinId();
- waitForWindowExposed(mainWin->windowHandle());
+ waitForWidgetMapped(mainWin);
}
close();
}
@@ -311,18 +316,33 @@ void QSplashScreen::drawContents(QPainter *painter)
}
}
+void QSplashScreenPrivate::handlePaintEvent()
+{
+ Q_Q(QSplashScreen);
+ QPainter painter(q);
+ painter.setRenderHints(QPainter::SmoothPixmapTransform);
+ painter.setLayoutDirection(q->layoutDirection());
+ if (!pixmap.isNull())
+ painter.drawPixmap(QPoint(), pixmap);
+ q->drawContents(&painter);
+}
+
/*! \reimp */
bool QSplashScreen::event(QEvent *e)
{
- if (e->type() == QEvent::Paint) {
- Q_D(QSplashScreen);
- QPainter painter(this);
- painter.setRenderHints(QPainter::SmoothPixmapTransform);
- painter.setLayoutDirection(layoutDirection());
- if (!d->pixmap.isNull())
- painter.drawPixmap(QPoint(), d->pixmap);
- drawContents(&painter);
+ Q_D(QSplashScreen);
+
+ switch (e->type()) {
+ case QEvent::Paint:
+ d->handlePaintEvent();
+ break;
+ case QEvent::Show:
+ waitForWidgetMapped(this);
+ break;
+ default:
+ break;
}
+
return QWidget::event(e);
}
diff --git a/src/widgets/widgets/qsplitter.cpp b/src/widgets/widgets/qsplitter.cpp
index 84c3799494..d0519a56a2 100644
--- a/src/widgets/widgets/qsplitter.cpp
+++ b/src/widgets/widgets/qsplitter.cpp
@@ -709,7 +709,7 @@ void QSplitterPrivate::setSizes_helper(const QList<int> &sizes, bool clampNegati
bool QSplitterPrivate::shouldShowWidget(const QWidget *w) const
{
Q_Q(const QSplitter);
- return q->isVisible() && !(w->isHidden() && w->testAttribute(Qt::WA_WState_ExplicitShowHide));
+ return q->isVisible() && !QWidgetPrivate::get(w)->isExplicitlyHidden();
}
void QSplitterPrivate::setGeo(QSplitterLayoutStruct *sls, int p, int s, bool allowCollapse)
diff --git a/src/widgets/widgets/qsplitter_p.h b/src/widgets/widgets/qsplitter_p.h
index 9fbd3ea520..5d5723a957 100644
--- a/src/widgets/widgets/qsplitter_p.h
+++ b/src/widgets/widgets/qsplitter_p.h
@@ -18,6 +18,8 @@
#include <QtWidgets/private/qtwidgetsglobal_p.h>
#include "private/qframe_p.h"
+#include <QtCore/qpointer.h>
+
QT_REQUIRE_CONFIG(splitter);
QT_BEGIN_NAMESPACE
diff --git a/src/widgets/widgets/qstackedwidget.cpp b/src/widgets/widgets/qstackedwidget.cpp
index 754744cf67..9cecee2ef8 100644
--- a/src/widgets/widgets/qstackedwidget.cpp
+++ b/src/widgets/widgets/qstackedwidget.cpp
@@ -98,8 +98,10 @@ QStackedWidget::QStackedWidget(QWidget *parent)
{
Q_D(QStackedWidget);
d->layout = new QStackedLayout(this);
- connect(d->layout, SIGNAL(widgetRemoved(int)), this, SIGNAL(widgetRemoved(int)));
- connect(d->layout, SIGNAL(currentChanged(int)), this, SIGNAL(currentChanged(int)));
+ connect(d->layout, &QStackedLayout::widgetRemoved,
+ this, &QStackedWidget::widgetRemoved);
+ connect(d->layout, &QStackedLayout::currentChanged,
+ this, &QStackedWidget::currentChanged);
}
/*!
diff --git a/src/widgets/widgets/qstatusbar.cpp b/src/widgets/widgets/qstatusbar.cpp
index 9a9879a48d..e398f52ac4 100644
--- a/src/widgets/widgets/qstatusbar.cpp
+++ b/src/widgets/widgets/qstatusbar.cpp
@@ -151,12 +151,12 @@ QRect QStatusBarPrivate::messageRect() const
Use the showMessage() slot to display a \e temporary message:
- \snippet mainwindows/dockwidgets/mainwindow.cpp 8
+ \snippet code/src_gui_widgets_qstatusbar.cpp 1
To remove a temporary message, use the clearMessage() slot, or set
a time limit when calling showMessage(). For example:
- \snippet mainwindows/dockwidgets/mainwindow.cpp 3
+ \snippet code/src_gui_widgets_qstatusbar.cpp 2
Use the currentMessage() function to retrieve the temporary
message currently shown. The QStatusBar class also provide the
@@ -179,7 +179,7 @@ QRect QStatusBarPrivate::messageRect() const
\image fusion-statusbar-sizegrip.png A status bar shown in the Fusion widget style
- \sa QMainWindow, QStatusTipEvent, {Qt Widgets - Application Example}
+ \sa QMainWindow, QStatusTipEvent
*/
@@ -271,7 +271,7 @@ int QStatusBar::insertWidget(int index, QWidget *widget, int stretch)
widget->hide();
reformat();
- if (!widget->isHidden() || !widget->testAttribute(Qt::WA_WState_ExplicitShowHide))
+ if (!QWidgetPrivate::get(widget)->isExplicitlyHidden())
widget->show();
return index;
@@ -286,7 +286,7 @@ int QStatusBar::insertWidget(int index, QWidget *widget, int stretch)
minimum of space.
Permanently means that the widget may not be obscured by temporary
- messages. It is is located at the far right of the status bar.
+ messages. It is located at the far right of the status bar.
\sa insertPermanentWidget(), removeWidget(), addWidget()
*/
@@ -298,7 +298,6 @@ void QStatusBar::addPermanentWidget(QWidget * widget, int stretch)
insertPermanentWidget(d_func()->items.size(), widget, stretch);
}
-
/*!
\since 4.2
@@ -313,7 +312,7 @@ void QStatusBar::addPermanentWidget(QWidget * widget, int stretch)
minimum of space.
Permanently means that the widget may not be obscured by temporary
- messages. It is is located at the far right of the status bar.
+ messages. It is located at the far right of the status bar.
\sa addPermanentWidget(), removeWidget(), addWidget()
*/
@@ -333,7 +332,7 @@ int QStatusBar::insertPermanentWidget(int index, QWidget *widget, int stretch)
d->items.insert(index, item);
reformat();
- if (!widget->isHidden() || !widget->testAttribute(Qt::WA_WState_ExplicitShowHide))
+ if (!QWidgetPrivate::get(widget)->isExplicitlyHidden())
widget->show();
return index;
@@ -355,19 +354,10 @@ void QStatusBar::removeWidget(QWidget *widget)
return;
Q_D(QStatusBar);
- bool found = false;
- for (int i = 0; i < d->items.size(); ++i) {
- const auto &item = d->items.at(i);
- if (item.widget == widget) {
- d->items.removeAt(i);
- item.widget->hide();
- found = true;
- break;
- }
- }
-
- if (found)
+ if (d->items.removeIf([widget](const auto &item) { return item.widget == widget; })) {
+ widget->hide();
reformat();
+ }
#if defined(QT_DEBUG)
else
qDebug("QStatusBar::removeWidget(): Widget not found.");
@@ -504,7 +494,7 @@ void QStatusBar::showMessage(const QString &message, int timeout)
if (timeout > 0) {
if (!d->timer) {
d->timer = new QTimer(this);
- connect(d->timer, SIGNAL(timeout()), this, SLOT(clearMessage()));
+ connect(d->timer, &QTimer::timeout, this, &QStatusBar::clearMessage);
}
d->timer->start(timeout);
} else if (d->timer) {
@@ -530,7 +520,7 @@ void QStatusBar::clearMessage()
if (d->tempItem.isEmpty())
return;
if (d->timer) {
- qDeleteInEventHandler(d->timer);
+ delete d->timer;
d->timer = nullptr;
}
d->tempItem.clear();
diff --git a/src/widgets/widgets/qtabbar.cpp b/src/widgets/widgets/qtabbar.cpp
index 0b1190ebf1..0b463840ae 100644
--- a/src/widgets/widgets/qtabbar.cpp
+++ b/src/widgets/widgets/qtabbar.cpp
@@ -71,14 +71,6 @@ void QMovableTabWidget::paintEvent(QPaintEvent *e)
p.drawPixmap(0, 0, m_pixmap);
}
-inline static bool verticalTabs(QTabBar::Shape shape)
-{
- return shape == QTabBar::RoundedWest
- || shape == QTabBar::RoundedEast
- || shape == QTabBar::TriangularWest
- || shape == QTabBar::TriangularEast;
-}
-
void QTabBarPrivate::updateMacBorderMetrics()
{
#if defined(Q_OS_MACOS)
@@ -283,9 +275,9 @@ void QTabBar::initStyleOption(QStyleOptionTab *option, int tabIndex) const
returns the visual geometry of a single tab.
\table 100%
- \row \li \inlineimage fusion-tabbar.png Screenshot of a Fusion style tab bar
+ \row \li \inlineimage {fusion-tabbar.png} {Screenshot of a Fusion style tab bar}
\li A tab bar shown in the \l{Qt Widget Gallery}{Fusion widget style}.
- \row \li \inlineimage fusion-tabbar-truncated.png Screenshot of a truncated Fusion tab bar
+ \row \li \inlineimage {fusion-tabbar-truncated.png} {Screenshot of a truncated Fusion tab bar}
\li A truncated tab bar shown in the Fusion widget style.
\endtable
@@ -374,12 +366,14 @@ void QTabBarPrivate::init()
leftB = new QToolButton(q);
leftB->setObjectName(u"ScrollLeftButton"_s);
leftB->setAutoRepeat(true);
- QObject::connect(leftB, SIGNAL(clicked()), q, SLOT(_q_scrollTabs()));
+ QObjectPrivate::connect(leftB, &QToolButton::clicked,
+ this, &QTabBarPrivate::scrollTabs);
leftB->hide();
rightB = new QToolButton(q);
rightB->setObjectName(u"ScrollRightButton"_s);
rightB->setAutoRepeat(true);
- QObject::connect(rightB, SIGNAL(clicked()), q, SLOT(_q_scrollTabs()));
+ QObjectPrivate::connect(rightB, &QToolButton::clicked,
+ this, &QTabBarPrivate::scrollTabs);
rightB->hide();
#ifdef QT_KEYPAD_NAVIGATION
if (QApplicationPrivate::keypadNavigationEnabled()) {
@@ -534,7 +528,6 @@ void QTabBarPrivate::layoutTabs()
if (useScrollButtons && tabList.size() && last > available) {
const QRect scrollRect = normalizedScrollRect(0);
- scrollOffset = -scrollRect.left();
Q_Q(QTabBar);
QStyleOption opt;
@@ -571,10 +564,9 @@ void QTabBarPrivate::layoutTabs()
leftB->show();
rightB->setGeometry(scrollButtonRightRect);
- rightB->setEnabled(last - scrollOffset > scrollRect.x() + scrollRect.width());
+ rightB->setEnabled(last + scrollRect.left() > scrollRect.x() + scrollRect.width());
rightB->show();
} else {
- scrollOffset = 0;
rightB->hide();
leftB->hide();
}
@@ -591,6 +583,11 @@ QRect QTabBarPrivate::normalizedScrollRect(int index)
// tab bar itself is in a different orientation.
Q_Q(QTabBar);
+ // If scrollbuttons are not visible, then there's no tear either, and
+ // the entire widget is the scroll rect.
+ if (leftB->isHidden())
+ return verticalTabs(shape) ? q->rect().transposed() : q->rect();
+
QStyleOptionTab opt;
q->initStyleOption(&opt, currentIndex);
opt.rect = q->rect();
@@ -670,25 +667,33 @@ int QTabBarPrivate::hoveredTabIndex() const
void QTabBarPrivate::makeVisible(int index)
{
Q_Q(QTabBar);
- if (!validIndex(index) || leftB->isHidden())
+ if (!validIndex(index))
return;
const QRect tabRect = tabList.at(index)->rect;
const int oldScrollOffset = scrollOffset;
const bool horiz = !verticalTabs(shape);
+ const int available = horiz ? q->width() : q->height();
const int tabStart = horiz ? tabRect.left() : tabRect.top();
const int tabEnd = horiz ? tabRect.right() : tabRect.bottom();
const int lastTabEnd = horiz ? tabList.constLast()->rect.right() : tabList.constLast()->rect.bottom();
const QRect scrollRect = normalizedScrollRect(index);
+ const QRect entireScrollRect = normalizedScrollRect(0); // ignore tears
const int scrolledTabBarStart = qMax(1, scrollRect.left() + scrollOffset);
const int scrolledTabBarEnd = qMin(lastTabEnd - 1, scrollRect.right() + scrollOffset);
- if (tabStart < scrolledTabBarStart) {
+ if (available >= lastTabEnd) {
+ // the entire tabbar fits, reset scroll
+ scrollOffset = 0;
+ } else if (tabStart < scrolledTabBarStart) {
// Tab is outside on the left, so scroll left.
scrollOffset = tabStart - scrollRect.left();
} else if (tabEnd > scrolledTabBarEnd) {
// Tab is outside on the right, so scroll right.
- scrollOffset = tabEnd - scrollRect.right();
+ scrollOffset = qMax(0, tabEnd - scrollRect.right());
+ } else if (scrollOffset + entireScrollRect.width() > lastTabEnd + 1) {
+ // fill any free space on the right without overshooting
+ scrollOffset = qMax(0, lastTabEnd - entireScrollRect.width() + 1);
}
leftB->setEnabled(scrollOffset > -scrollRect.left());
@@ -762,7 +767,7 @@ void QTabBarPrivate::autoHideTabs()
q->setVisible(q->count() > 1);
}
-void QTabBarPrivate::_q_closeTab()
+void QTabBarPrivate::closeTab()
{
Q_Q(QTabBar);
QObject *object = q->sender();
@@ -785,7 +790,7 @@ void QTabBarPrivate::_q_closeTab()
emit q->tabCloseRequested(tabToClose);
}
-void QTabBarPrivate::_q_scrollTabs()
+void QTabBarPrivate::scrollTabs()
{
Q_Q(QTabBar);
const QObject *sender = q->sender();
@@ -975,7 +980,8 @@ int QTabBar::insertTab(int index, const QIcon& icon, const QString &text)
initStyleOption(&opt, index);
ButtonPosition closeSide = (ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, nullptr, this);
QAbstractButton *closeButton = new CloseButton(this);
- connect(closeButton, SIGNAL(clicked()), this, SLOT(_q_closeTab()));
+ QObjectPrivate::connect(closeButton, &CloseButton::clicked,
+ d, &QTabBarPrivate::closeTab);
setTabButton(index, closeSide, closeButton);
}
@@ -1410,7 +1416,10 @@ void QTabBar::setCurrentIndex(int index)
if (tabRect(index).size() != tabSizeHint(index))
d->layoutTabs();
update();
- d->makeVisible(index);
+ if (!isVisible())
+ d->layoutDirty = true;
+ else
+ d->makeVisible(index);
if (d->validIndex(oldIndex)) {
tab->lastTab = oldIndex;
d->layoutTab(oldIndex);
@@ -1645,6 +1654,8 @@ void QTabBar::showEvent(QShowEvent *)
d->refresh();
if (!d->validIndex(d->currentIndex))
setCurrentIndex(0);
+ else
+ d->makeVisible(d->currentIndex);
d->updateMacBorderMetrics();
}
@@ -1778,6 +1789,8 @@ void QTabBar::resizeEvent(QResizeEvent *)
Q_D(QTabBar);
if (d->layoutDirty)
updateGeometry();
+
+ // when resizing, we want to keep the scroll offset as much as possible
d->layoutTabs();
d->makeVisible(d->currentIndex);
@@ -1873,21 +1886,30 @@ void QTabBar::paintEvent(QPaintEvent *)
QStyleOptionTab tabOption;
const auto tab = d->tabList.at(selected);
initStyleOption(&tabOption, selected);
+
if (d->paintWithOffsets && tab->dragOffset != 0) {
+ // if the drag offset is != 0, a move is in progress (drag or animation)
+ // => set the tab position to Moving to preserve the rect
+ tabOption.position = QStyleOptionTab::TabPosition::Moving;
+
if (vertical)
tabOption.rect.moveTop(tabOption.rect.y() + tab->dragOffset);
else
tabOption.rect.moveLeft(tabOption.rect.x() + tab->dragOffset);
}
- if (!d->dragInProgress)
- p.drawControl(QStyle::CE_TabBarTab, tabOption);
- else {
- int taboverlap = style()->pixelMetric(QStyle::PM_TabBarTabOverlap, nullptr, this);
- if (verticalTabs(d->shape))
- d->movingTab->setGeometry(tabOption.rect.adjusted(0, -taboverlap, 0, taboverlap));
- else
- d->movingTab->setGeometry(tabOption.rect.adjusted(-taboverlap, 0, taboverlap, 0));
- }
+
+ // Calculate the rect of a moving tab
+ const int taboverlap = style()->pixelMetric(QStyle::PM_TabBarTabOverlap, nullptr, this);
+ const QRect &movingRect = verticalTabs(d->shape)
+ ? tabOption.rect.adjusted(0, -taboverlap, 0, taboverlap)
+ : tabOption.rect.adjusted(-taboverlap, 0, taboverlap, 0);
+
+ // If a drag is in process, set the moving tab's geometry here
+ // (in an animation, it is already set)
+ if (d->dragInProgress)
+ d->movingTab->setGeometry(movingRect);
+
+ p.drawControl(QStyle::CE_TabBarTab, tabOption);
}
// Only draw the tear indicator if necessary. Most of the time we don't need too.
@@ -2194,7 +2216,8 @@ void QTabBar::mouseMoveEvent(QMouseEvent *event)
}
}
// Buttons needs to follow the dragged tab
- d->layoutTab(d->pressedIndex);
+ if (d->pressedIndex != -1)
+ d->layoutTab(d->pressedIndex);
update();
}
@@ -2226,7 +2249,7 @@ void QTabBarPrivate::setupMovableTab()
QStyleOptionTab tab;
q->initStyleOption(&tab, pressedIndex);
- tab.position = QStyleOptionTab::OnlyOneTab;
+ tab.position = QStyleOptionTab::Moving;
if (verticalTabs(shape))
tab.rect.moveTopLeft(QPoint(0, taboverlap));
else
@@ -2416,7 +2439,7 @@ void QTabBarPrivate::setCurrentNextEnabledIndex(int offset)
{
Q_Q(QTabBar);
for (int index = currentIndex + offset; validIndex(index); index += offset) {
- if (tabList.at(index)->enabled) {
+ if (tabList.at(index)->enabled && tabList.at(index)->visible) {
q->setCurrentIndex(index);
break;
}
@@ -2562,7 +2585,8 @@ void QTabBar::setTabsClosable(bool closable)
continue;
newButtons = true;
QAbstractButton *closeButton = new CloseButton(this);
- connect(closeButton, SIGNAL(clicked()), this, SLOT(_q_closeTab()));
+ QObjectPrivate::connect(closeButton, &CloseButton::clicked,
+ d, &QTabBarPrivate::closeTab);
setTabButton(i, closeSide, closeButton);
}
if (newButtons)
diff --git a/src/widgets/widgets/qtabbar.h b/src/widgets/widgets/qtabbar.h
index 2d3179e2d0..2759d5a318 100644
--- a/src/widgets/widgets/qtabbar.h
+++ b/src/widgets/widgets/qtabbar.h
@@ -185,8 +185,6 @@ protected:
private:
Q_DISABLE_COPY(QTabBar)
Q_DECLARE_PRIVATE(QTabBar)
- Q_PRIVATE_SLOT(d_func(), void _q_scrollTabs())
- Q_PRIVATE_SLOT(d_func(), void _q_closeTab())
};
QT_END_NAMESPACE
diff --git a/src/widgets/widgets/qtabbar_p.h b/src/widgets/widgets/qtabbar_p.h
index a421836b93..d76f439bd7 100644
--- a/src/widgets/widgets/qtabbar_p.h
+++ b/src/widgets/widgets/qtabbar_p.h
@@ -187,8 +187,8 @@ public:
inline bool validIndex(int index) const { return index >= 0 && index < tabList.size(); }
void setCurrentNextEnabledIndex(int offset);
- void _q_scrollTabs();
- void _q_closeTab();
+ void scrollTabs();
+ void closeTab();
void moveTab(int index, int offset);
void moveTabFinished(int index);
@@ -245,6 +245,14 @@ public:
};
+constexpr inline bool verticalTabs(QTabBar::Shape shape) noexcept
+{
+ return shape == QTabBar::RoundedWest
+ || shape == QTabBar::RoundedEast
+ || shape == QTabBar::TriangularWest
+ || shape == QTabBar::TriangularEast;
+}
+
QT_END_NAMESPACE
#endif
diff --git a/src/widgets/widgets/qtabwidget.cpp b/src/widgets/widgets/qtabwidget.cpp
index 89e3d59a06..5e7c266ef6 100644
--- a/src/widgets/widgets/qtabwidget.cpp
+++ b/src/widgets/widgets/qtabwidget.cpp
@@ -156,9 +156,9 @@ public:
QTabWidgetPrivate();
~QTabWidgetPrivate();
void updateTabBarPosition();
- void _q_showTab(int);
- void _q_removeTab(int);
- void _q_tabMoved(int from, int to);
+ void showTab(int);
+ void removeTab(int);
+ void tabMoved(int from, int to);
void init();
bool isAutoHidden() const
{
@@ -197,7 +197,7 @@ void QTabWidgetPrivate::init()
// hack so that QMacStyle::layoutSpacing() can detect tab widget pages
stack->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred, QSizePolicy::TabWidget));
- QObject::connect(stack, SIGNAL(widgetRemoved(int)), q, SLOT(_q_removeTab(int)));
+ QObjectPrivate::connect(stack, &QStackedWidget::widgetRemoved, this, &QTabWidgetPrivate::removeTab);
QTabBar *tabBar = new QTabBar(q);
tabBar->setObjectName("qt_tabwidget_tabbar"_L1);
tabBar->setDrawBase(false);
@@ -720,17 +720,17 @@ void QTabWidget::setTabBar(QTabBar* tb)
delete d->tabs;
d->tabs = tb;
setFocusProxy(d->tabs);
- connect(d->tabs, SIGNAL(currentChanged(int)),
- this, SLOT(_q_showTab(int)));
- connect(d->tabs, SIGNAL(tabMoved(int,int)),
- this, SLOT(_q_tabMoved(int,int)));
- connect(d->tabs, SIGNAL(tabBarClicked(int)),
- this, SIGNAL(tabBarClicked(int)));
- connect(d->tabs, SIGNAL(tabBarDoubleClicked(int)),
- this, SIGNAL(tabBarDoubleClicked(int)));
+ QObjectPrivate::connect(d->tabs, &QTabBar::currentChanged,
+ d, &QTabWidgetPrivate::showTab);
+ QObjectPrivate::connect(d->tabs, &QTabBar::tabMoved,
+ d, &QTabWidgetPrivate::tabMoved);
+ connect(d->tabs, &QTabBar::tabBarClicked,
+ this, &QTabWidget::tabBarClicked);
+ connect(d->tabs, &QTabBar::tabBarDoubleClicked,
+ this, &QTabWidget::tabBarDoubleClicked);
if (d->tabs->tabsClosable())
- connect(d->tabs, SIGNAL(tabCloseRequested(int)),
- this, SIGNAL(tabCloseRequested(int)));
+ connect(d->tabs, &QTabBar::tabCloseRequested,
+ this, &QTabWidget::tabCloseRequested);
tb->setExpanding(!documentMode());
setUpLayout();
}
@@ -752,7 +752,7 @@ QTabBar* QTabWidget::tabBar() const
sized.
*/
-void QTabWidgetPrivate::_q_showTab(int index)
+void QTabWidgetPrivate::showTab(int index)
{
Q_Q(QTabWidget);
if (index < stack->count() && index >= 0)
@@ -760,7 +760,7 @@ void QTabWidgetPrivate::_q_showTab(int index)
emit q->currentChanged(index);
}
-void QTabWidgetPrivate::_q_removeTab(int index)
+void QTabWidgetPrivate::removeTab(int index)
{
Q_Q(QTabWidget);
tabs->removeTab(index);
@@ -768,7 +768,7 @@ void QTabWidgetPrivate::_q_removeTab(int index)
q->tabRemoved(index);
}
-void QTabWidgetPrivate::_q_tabMoved(int from, int to)
+void QTabWidgetPrivate::tabMoved(int from, int to)
{
const QSignalBlocker blocker(stack);
QWidget *w = stack->widget(from);
@@ -1139,7 +1139,7 @@ void QTabWidget::keyPressEvent(QKeyEvent *e)
) {
page = 0;
}
- if (d->tabs->isTabEnabled(page)) {
+ if (d->tabs->isTabEnabled(page) && d->tabs->isTabVisible(page)) {
setCurrentIndex(page);
break;
}
@@ -1407,6 +1407,20 @@ void QTabWidget::clear()
removeTab(0);
}
+QTabBar::Shape _q_tb_tabBarShapeFrom(QTabWidget::TabShape shape, QTabWidget::TabPosition position)
+{
+ const bool rounded = (shape == QTabWidget::Rounded);
+ if (position == QTabWidget::North)
+ return rounded ? QTabBar::RoundedNorth : QTabBar::TriangularNorth;
+ if (position == QTabWidget::South)
+ return rounded ? QTabBar::RoundedSouth : QTabBar::TriangularSouth;
+ if (position == QTabWidget::East)
+ return rounded ? QTabBar::RoundedEast : QTabBar::TriangularEast;
+ if (position == QTabWidget::West)
+ return rounded ? QTabBar::RoundedWest : QTabBar::TriangularWest;
+ return QTabBar::RoundedNorth;
+}
+
QT_END_NAMESPACE
#include "moc_qtabwidget.cpp"
diff --git a/src/widgets/widgets/qtabwidget.h b/src/widgets/widgets/qtabwidget.h
index 6188ea4999..c813f8d921 100644
--- a/src/widgets/widgets/qtabwidget.h
+++ b/src/widgets/widgets/qtabwidget.h
@@ -141,9 +141,6 @@ protected:
private:
Q_DECLARE_PRIVATE(QTabWidget)
Q_DISABLE_COPY(QTabWidget)
- Q_PRIVATE_SLOT(d_func(), void _q_showTab(int))
- Q_PRIVATE_SLOT(d_func(), void _q_removeTab(int))
- Q_PRIVATE_SLOT(d_func(), void _q_tabMoved(int, int))
void setUpLayout(bool = false);
};
diff --git a/src/widgets/widgets/qtextbrowser.cpp b/src/widgets/widgets/qtextbrowser.cpp
index 1cac46e8a8..2c01ed2b26 100644
--- a/src/widgets/widgets/qtextbrowser.cpp
+++ b/src/widgets/widgets/qtextbrowser.cpp
@@ -46,6 +46,11 @@ public:
, lastKeypadScrollValue(-1)
#endif
{}
+ ~QTextBrowserPrivate()
+ {
+ for (const QMetaObject::Connection &connection : connections)
+ QObject::disconnect(connection);
+ }
void init();
@@ -103,14 +108,14 @@ public:
QString findFile(const QUrl &name) const;
- inline void _q_documentModified()
+ inline void documentModified()
{
textOrSourceChanged = true;
forceLoadOnSourceChange = !currentURL.path().isEmpty();
}
- void _q_activateAnchor(const QString &href);
- void _q_highlightLink(const QString &href);
+ void activateAnchor(const QString &href);
+ void highlightLink(const QString &href);
void setSource(const QUrl &url, QTextDocument::ResourceType type);
@@ -129,6 +134,7 @@ public:
Q_Q(QTextBrowser);
emit q->highlighted(url);
}
+ std::array<QMetaObject::Connection, 3> connections;
};
Q_DECLARE_TYPEINFO(QTextBrowserPrivate::HistoryEntry, Q_RELOCATABLE_TYPE);
@@ -190,7 +196,7 @@ QUrl QTextBrowserPrivate::resolveUrl(const QUrl &url) const
return url;
}
-void QTextBrowserPrivate::_q_activateAnchor(const QString &href)
+void QTextBrowserPrivate::activateAnchor(const QString &href)
{
if (href.isEmpty())
return;
@@ -231,7 +237,7 @@ void QTextBrowserPrivate::_q_activateAnchor(const QString &href)
q->setSource(url);
}
-void QTextBrowserPrivate::_q_highlightLink(const QString &anchor)
+void QTextBrowserPrivate::highlightLink(const QString &anchor)
{
if (anchor.isEmpty()) {
#ifndef QT_NO_CURSOR
@@ -670,11 +676,14 @@ void QTextBrowserPrivate::init()
q->setAttribute(Qt::WA_InputMethodEnabled, shouldEnableInputMethod(q));
q->setUndoRedoEnabled(false);
viewport->setMouseTracking(true);
- QObject::connect(q->document(), SIGNAL(contentsChanged()), q, SLOT(_q_documentModified()));
- QObject::connect(control, SIGNAL(linkActivated(QString)),
- q, SLOT(_q_activateAnchor(QString)));
- QObject::connect(control, SIGNAL(linkHovered(QString)),
- q, SLOT(_q_highlightLink(QString)));
+ connections = {
+ QObjectPrivate::connect(q->document(), &QTextDocument::contentsChanged,
+ this, &QTextBrowserPrivate::documentModified),
+ QObjectPrivate::connect(control, &QWidgetTextControl::linkActivated,
+ this, &QTextBrowserPrivate::activateAnchor),
+ QObjectPrivate::connect(control, &QWidgetTextControl::linkHovered,
+ this, &QTextBrowserPrivate::highlightLink),
+ };
}
/*!
diff --git a/src/widgets/widgets/qtextbrowser.h b/src/widgets/widgets/qtextbrowser.h
index 1a6cd412d1..f8e9c0c301 100644
--- a/src/widgets/widgets/qtextbrowser.h
+++ b/src/widgets/widgets/qtextbrowser.h
@@ -82,9 +82,6 @@ protected:
private:
Q_DISABLE_COPY(QTextBrowser)
Q_DECLARE_PRIVATE(QTextBrowser)
- Q_PRIVATE_SLOT(d_func(), void _q_documentModified())
- Q_PRIVATE_SLOT(d_func(), void _q_activateAnchor(const QString &))
- Q_PRIVATE_SLOT(d_func(), void _q_highlightLink(const QString &))
};
QT_END_NAMESPACE
diff --git a/src/widgets/widgets/qtextedit.cpp b/src/widgets/widgets/qtextedit.cpp
index f3d1d3e511..90884dd438 100644
--- a/src/widgets/widgets/qtextedit.cpp
+++ b/src/widgets/widgets/qtextedit.cpp
@@ -88,8 +88,7 @@ QTextEditPrivate::QTextEditPrivate()
: control(nullptr),
autoFormatting(QTextEdit::AutoNone), tabChangesFocus(false),
lineWrap(QTextEdit::WidgetWidth), lineWrapColumnOrWidth(0),
- wordWrap(QTextOption::WrapAtWordBoundaryOrAnywhere), clickCausedFocus(0),
- textFormat(Qt::AutoText)
+ wordWrap(QTextOption::WrapAtWordBoundaryOrAnywhere), clickCausedFocus(0)
{
ignoreAutomaticScrollbarAdjustment = false;
preferRichText = false;
@@ -97,6 +96,12 @@ QTextEditPrivate::QTextEditPrivate()
inDrag = false;
}
+QTextEditPrivate::~QTextEditPrivate()
+{
+ for (const QMetaObject::Connection &connection : connections)
+ QObject::disconnect(connection);
+}
+
void QTextEditPrivate::createAutoBulletList()
{
QTextCursor cursor = control->textCursor();
@@ -123,24 +128,34 @@ void QTextEditPrivate::init(const QString &html)
control = new QTextEditControl(q);
control->setPalette(q->palette());
- QObject::connect(control, SIGNAL(microFocusChanged()), q, SLOT(updateMicroFocus()));
- QObject::connect(control, SIGNAL(documentSizeChanged(QSizeF)), q, SLOT(_q_adjustScrollbars()));
- QObject::connect(control, SIGNAL(updateRequest(QRectF)), q, SLOT(_q_repaintContents(QRectF)));
- QObject::connect(control, SIGNAL(visibilityRequest(QRectF)), q, SLOT(_q_ensureVisible(QRectF)));
- QObject::connect(control, SIGNAL(currentCharFormatChanged(QTextCharFormat)),
- q, SLOT(_q_currentCharFormatChanged(QTextCharFormat)));
-
- QObject::connect(control, SIGNAL(textChanged()), q, SIGNAL(textChanged()));
- QObject::connect(control, SIGNAL(undoAvailable(bool)), q, SIGNAL(undoAvailable(bool)));
- QObject::connect(control, SIGNAL(redoAvailable(bool)), q, SIGNAL(redoAvailable(bool)));
- QObject::connect(control, SIGNAL(copyAvailable(bool)), q, SIGNAL(copyAvailable(bool)));
- QObject::connect(control, SIGNAL(selectionChanged()), q, SIGNAL(selectionChanged()));
- QObject::connect(control, SIGNAL(cursorPositionChanged()), q, SLOT(_q_cursorPositionChanged()));
-#if QT_CONFIG(cursor)
- QObject::connect(control, SIGNAL(blockMarkerHovered(QTextBlock)), q, SLOT(_q_hoveredBlockWithMarkerChanged(QTextBlock)));
-#endif
-
- QObject::connect(control, SIGNAL(textChanged()), q, SLOT(updateMicroFocus()));
+ connections = {
+ QObjectPrivate::connect(control, &QTextEditControl::documentSizeChanged,
+ this, &QTextEditPrivate::adjustScrollbars),
+ QObjectPrivate::connect(control, &QTextEditControl::updateRequest,
+ this, &QTextEditPrivate::repaintContents),
+ QObjectPrivate::connect(control, &QTextEditControl::visibilityRequest,
+ this, &QTextEditPrivate::ensureVisible),
+ QObjectPrivate::connect(control, &QTextEditControl::blockMarkerHovered,
+ this, &QTextEditPrivate::hoveredBlockWithMarkerChanged),
+ QObjectPrivate::connect(control, &QTextEditControl::cursorPositionChanged,
+ this, &QTextEditPrivate::cursorPositionChanged),
+ QObject::connect(control, &QTextEditControl::microFocusChanged,
+ q, [q]() { q->updateMicroFocus(); }),
+ QObject::connect(control, &QTextEditControl::currentCharFormatChanged,
+ q, &QTextEdit::currentCharFormatChanged),
+ QObject::connect(control, &QTextEditControl::textChanged,
+ q, &QTextEdit::textChanged),
+ QObject::connect(control, &QTextEditControl::undoAvailable,
+ q, &QTextEdit::undoAvailable),
+ QObject::connect(control, &QTextEditControl::redoAvailable,
+ q, &QTextEdit::redoAvailable),
+ QObject::connect(control, &QTextEditControl::copyAvailable,
+ q, &QTextEdit::copyAvailable),
+ QObject::connect(control, &QTextEditControl::selectionChanged,
+ q, &QTextEdit::selectionChanged),
+ QObject::connect(control, &QTextEditControl::textChanged,
+ q, [q]() { q->updateMicroFocus(); }),
+ };
QTextDocument *doc = control->document();
// set a null page size initially to avoid any relayouting until the textedit
@@ -170,7 +185,7 @@ void QTextEditPrivate::init(const QString &html)
#endif
}
-void QTextEditPrivate::_q_repaintContents(const QRectF &contentsRect)
+void QTextEditPrivate::repaintContents(const QRectF &contentsRect)
{
if (!contentsRect.isValid()) {
viewport->update();
@@ -188,7 +203,7 @@ void QTextEditPrivate::_q_repaintContents(const QRectF &contentsRect)
viewport->update(r);
}
-void QTextEditPrivate::_q_cursorPositionChanged()
+void QTextEditPrivate::cursorPositionChanged()
{
Q_Q(QTextEdit);
emit q->cursorPositionChanged();
@@ -198,9 +213,9 @@ void QTextEditPrivate::_q_cursorPositionChanged()
#endif
}
-#if QT_CONFIG(cursor)
-void QTextEditPrivate::_q_hoveredBlockWithMarkerChanged(const QTextBlock &block)
+void QTextEditPrivate::hoveredBlockWithMarkerChanged(const QTextBlock &block)
{
+#if QT_CONFIG(cursor)
Q_Q(QTextEdit);
Qt::CursorShape cursor = cursorToRestoreAfterHover;
if (block.isValid() && !q->isReadOnly()) {
@@ -212,8 +227,8 @@ void QTextEditPrivate::_q_hoveredBlockWithMarkerChanged(const QTextBlock &block)
}
}
viewport->setCursor(cursor);
-}
#endif
+}
void QTextEditPrivate::pageUpDown(QTextCursor::MoveOperation op, QTextCursor::MoveMode moveMode)
{
@@ -262,7 +277,7 @@ static QSize documentSize(QWidgetTextControl *control)
return docSize;
}
-void QTextEditPrivate::_q_adjustScrollbars()
+void QTextEditPrivate::adjustScrollbars()
{
if (ignoreAutomaticScrollbarAdjustment)
return;
@@ -312,12 +327,12 @@ void QTextEditPrivate::_q_adjustScrollbars()
#endif
// rect is in content coordinates
-void QTextEditPrivate::_q_ensureVisible(const QRectF &_rect)
+void QTextEditPrivate::ensureVisible(const QRectF &_rect)
{
const QRect rect = _rect.toRect();
if ((vbar->isVisible() && vbar->maximum() < rect.bottom())
|| (hbar->isVisible() && hbar->maximum() < rect.right()))
- _q_adjustScrollbars();
+ adjustScrollbars();
const int visibleWidth = viewport->width();
const int visibleHeight = viewport->height();
const bool rtl = q_func()->isRightToLeft();
@@ -540,7 +555,7 @@ void QTextEditPrivate::_q_ensureVisible(const QRectF &_rect)
of the movement keystrokes, for example, \e{Shift+Right}
will select the character to the right, and \e{Shift+Ctrl+Right} will select the word to the right, etc.
- \sa QTextDocument, QTextCursor, {Qt Widgets - Application Example},
+ \sa QTextDocument, QTextCursor,
{Syntax Highlighter Example}, {Rich Text Processing}
*/
@@ -713,7 +728,8 @@ QColor QTextEdit::textColor() const
QColor QTextEdit::textBackgroundColor() const
{
Q_D(const QTextEdit);
- return d->control->textCursor().charFormat().background().color();
+ const QBrush &brush = d->control->textCursor().charFormat().background();
+ return brush.style() == Qt::NoBrush ? Qt::transparent : brush.color();
}
/*!
@@ -1064,30 +1080,38 @@ void QTextEdit::selectAll()
bool QTextEdit::event(QEvent *e)
{
Q_D(QTextEdit);
-#ifndef QT_NO_CONTEXTMENU
- if (e->type() == QEvent::ContextMenu
- && static_cast<QContextMenuEvent *>(e)->reason() == QContextMenuEvent::Keyboard) {
- Q_D(QTextEdit);
- ensureCursorVisible();
- const QPoint cursorPos = cursorRect().center();
- QContextMenuEvent ce(QContextMenuEvent::Keyboard, cursorPos, d->viewport->mapToGlobal(cursorPos));
- ce.setAccepted(e->isAccepted());
- const bool result = QAbstractScrollArea::event(&ce);
- e->setAccepted(ce.isAccepted());
- return result;
- } else if (e->type() == QEvent::ShortcutOverride
- || e->type() == QEvent::ToolTip) {
+ switch (e->type()) {
+ case QEvent::ShortcutOverride:
+ case QEvent::ToolTip:
d->sendControlEvent(e);
- }
-#else
- Q_UNUSED(d);
+ break;
+ case QEvent::WindowActivate:
+ case QEvent::WindowDeactivate:
+ d->control->setPalette(palette());
+ break;
+#ifndef QT_NO_CONTEXTMENU
+ case QEvent::ContextMenu:
+ if (static_cast<QContextMenuEvent *>(e)->reason() == QContextMenuEvent::Keyboard) {
+ ensureCursorVisible();
+ const QPoint cursorPos = cursorRect().center();
+ QContextMenuEvent ce(QContextMenuEvent::Keyboard, cursorPos, d->viewport->mapToGlobal(cursorPos));
+ ce.setAccepted(e->isAccepted());
+ const bool result = QAbstractScrollArea::event(&ce);
+ e->setAccepted(ce.isAccepted());
+ return result;
+ }
+ break;
#endif // QT_NO_CONTEXTMENU
#ifdef QT_KEYPAD_NAVIGATION
- if (e->type() == QEvent::EnterEditFocus || e->type() == QEvent::LeaveEditFocus) {
+ case QEvent::EnterEditFocus:
+ case QEvent::LeaveEditFocus:
if (QApplicationPrivate::keypadNavigationEnabled())
d->sendControlEvent(e);
- }
+ break;
#endif
+ default:
+ break;
+ }
return QAbstractScrollArea::event(e);
}
@@ -1477,7 +1501,7 @@ void QTextEdit::resizeEvent(QResizeEvent *e)
&& alignmentProperty.userType() == QMetaType::Bool
&& !alignmentProperty.toBool()) {
- d->_q_adjustScrollbars();
+ d->adjustScrollbars();
return;
}
}
@@ -1486,7 +1510,7 @@ void QTextEdit::resizeEvent(QResizeEvent *e)
&& e->oldSize().width() != e->size().width())
d->relayoutDocument();
else
- d->_q_adjustScrollbars();
+ d->adjustScrollbars();
}
void QTextEditPrivate::relayoutDocument()
@@ -1508,7 +1532,7 @@ void QTextEditPrivate::relayoutDocument()
else
lastUsedSize = layout->documentSize().toSize();
- // ignore calls to _q_adjustScrollbars caused by an emission of the
+ // ignore calls to adjustScrollbars caused by an emission of the
// usedSizeChanged() signal in the layout, as we're calling it
// later on our own anyway (or deliberately not) .
const bool oldIgnoreScrollbarAdjustment = ignoreAutomaticScrollbarAdjustment;
@@ -1547,7 +1571,7 @@ void QTextEditPrivate::relayoutDocument()
// its size. So a layout with less width _can_ take up less vertical space, too.
// If the wider case causes a vertical scroll bar to appear and the narrower one
// (narrower because the vertical scroll bar takes up horizontal space)) to disappear
- // again then we have an endless loop, as _q_adjustScrollBars sets new ranges on the
+ // again then we have an endless loop, as adjustScrollbars sets new ranges on the
// scroll bars, the QAbstractScrollArea will find out about it and try to show/hide
// the scroll bars again. That's why we try to detect this case here and break out.
//
@@ -1560,7 +1584,7 @@ void QTextEditPrivate::relayoutDocument()
&& usedSize.height() <= viewport->height())
return;
- _q_adjustScrollbars();
+ adjustScrollbars();
}
void QTextEditPrivate::paint(QPainter *p, QPaintEvent *e)
@@ -1598,6 +1622,8 @@ void QTextEditPrivate::paint(QPainter *p, QPaintEvent *e)
This event handler can be reimplemented in a subclass to receive paint events passed in \a event.
It is usually unnecessary to reimplement this function in a subclass of QTextEdit.
+\note If you create a QPainter, it must operate on the \l{QAbstractScrollArea::}{viewport()}.
+
\warning The underlying text document must not be modified from within a reimplementation
of this function.
*/
@@ -1608,12 +1634,6 @@ void QTextEdit::paintEvent(QPaintEvent *e)
d->paint(&p, e);
}
-void QTextEditPrivate::_q_currentCharFormatChanged(const QTextCharFormat &fmt)
-{
- Q_Q(QTextEdit);
- emit q->currentCharFormatChanged(fmt);
-}
-
void QTextEditPrivate::updateDefaultTextOption()
{
QTextDocument *doc = control->document();
@@ -1804,7 +1824,7 @@ QVariant QTextEdit::inputMethodQuery(Qt::InputMethodQuery query, QVariant argume
Q_D(const QTextEdit);
switch (query) {
case Qt::ImEnabled:
- return isEnabled();
+ return isEnabled() && !isReadOnly();
case Qt::ImHints:
case Qt::ImInputItemClipRectangle:
return QWidget::inputMethodQuery(query);
@@ -2209,7 +2229,7 @@ void QTextEdit::insertFromMimeData(const QMimeData *source)
bool QTextEdit::isReadOnly() const
{
Q_D(const QTextEdit);
- return !(d->control->textInteractionFlags() & Qt::TextEditable);
+ return !d->control || !(d->control->textInteractionFlags() & Qt::TextEditable);
}
void QTextEdit::setReadOnly(bool ro)
@@ -2366,7 +2386,7 @@ void QTextEdit::scrollToAnchor(const QString &name)
QPointF p = d->control->anchorPosition(name);
const int newPosition = qRound(p.y());
if ( d->vbar->maximum() < newPosition )
- d->_q_adjustScrollbars();
+ d->adjustScrollbars();
d->vbar->setValue(newPosition);
}
@@ -2580,11 +2600,14 @@ bool QTextEdit::find(const QString &exp, QTextDocument::FindFlags options)
\overload
Finds the next occurrence, matching the regular expression, \a exp, using the given
- \a options. The QTextDocument::FindCaseSensitively option is ignored for this overload,
- use QRegularExpression::CaseInsensitiveOption instead.
+ \a options.
Returns \c true if a match was found and changes the cursor to select the match;
otherwise returns \c false.
+
+ \warning For historical reasons, the case sensitivity option set on
+ \a exp is ignored. Instead, the \a options are used to determine
+ if the search is case sensitive or not.
*/
#if QT_CONFIG(regularexpression)
bool QTextEdit::find(const QRegularExpression &exp, QTextDocument::FindFlags options)
@@ -2648,10 +2671,7 @@ bool QTextEdit::find(const QRegularExpression &exp, QTextDocument::FindFlags opt
*/
void QTextEdit::setText(const QString &text)
{
- Q_D(QTextEdit);
- Qt::TextFormat format = d->textFormat;
- if (d->textFormat == Qt::AutoText)
- format = Qt::mightBeRichText(text) ? Qt::RichText : Qt::PlainText;
+ Qt::TextFormat format = Qt::mightBeRichText(text) ? Qt::RichText : Qt::PlainText;
#ifndef QT_NO_TEXTHTMLPARSER
if (format == Qt::RichText)
setHtml(text);
diff --git a/src/widgets/widgets/qtextedit.h b/src/widgets/widgets/qtextedit.h
index eed0d4e7e9..421eb9136b 100644
--- a/src/widgets/widgets/qtextedit.h
+++ b/src/widgets/widgets/qtextedit.h
@@ -280,14 +280,6 @@ protected:
private:
Q_DISABLE_COPY(QTextEdit)
- Q_PRIVATE_SLOT(d_func(), void _q_repaintContents(const QRectF &r))
- Q_PRIVATE_SLOT(d_func(), void _q_currentCharFormatChanged(const QTextCharFormat &))
- Q_PRIVATE_SLOT(d_func(), void _q_adjustScrollbars())
- Q_PRIVATE_SLOT(d_func(), void _q_ensureVisible(const QRectF &))
- Q_PRIVATE_SLOT(d_func(), void _q_cursorPositionChanged())
-#if QT_CONFIG(cursor)
- Q_PRIVATE_SLOT(d_func(), void _q_hoveredBlockWithMarkerChanged(const QTextBlock &))
-#endif
friend class QTextEditControl;
friend class QTextDocument;
friend class QWidgetTextControl;
diff --git a/src/widgets/widgets/qtextedit_p.h b/src/widgets/widgets/qtextedit_p.h
index 1200b7470e..7dd7d9c107 100644
--- a/src/widgets/widgets/qtextedit_p.h
+++ b/src/widgets/widgets/qtextedit_p.h
@@ -33,6 +33,8 @@
#include "private/qwidgettextcontrol_p.h"
+#include <array>
+
QT_REQUIRE_CONFIG(textedit);
QT_BEGIN_NAMESPACE
@@ -43,16 +45,17 @@ class QTextEditPrivate : public QAbstractScrollAreaPrivate
Q_DECLARE_PUBLIC(QTextEdit)
public:
QTextEditPrivate();
+ ~QTextEditPrivate();
void init(const QString &html = QString());
void paint(QPainter *p, QPaintEvent *e);
- void _q_repaintContents(const QRectF &contentsRect);
+ void repaintContents(const QRectF &contentsRect);
inline QPoint mapToContents(const QPoint &point) const
{ return QPoint(point.x() + horizontalOffset(), point.y() + verticalOffset()); }
- void _q_adjustScrollbars();
- void _q_ensureVisible(const QRectF &rect);
+ void adjustScrollbars();
+ void ensureVisible(const QRectF &rect);
void relayoutDocument();
void createAutoBulletList();
@@ -66,9 +69,8 @@ public:
inline void sendControlEvent(QEvent *e)
{ control->processEvent(e, QPointF(horizontalOffset(), verticalOffset()), viewport); }
- void _q_currentCharFormatChanged(const QTextCharFormat &format);
- void _q_cursorPositionChanged();
- void _q_hoveredBlockWithMarkerChanged(const QTextBlock &block);
+ void cursorPositionChanged();
+ void hoveredBlockWithMarkerChanged(const QTextBlock &block);
void updateDefaultTextOption();
@@ -94,15 +96,14 @@ public:
uint inDrag : 1;
uint clickCausedFocus : 1;
- // Qt3 COMPAT only, for setText
- Qt::TextFormat textFormat;
-
QString anchorToScrollToWhenVisible;
QString placeholderText;
Qt::CursorShape cursorToRestoreAfterHover = Qt::IBeamCursor;
+ std::array<QMetaObject::Connection, 13> connections;
+
#ifdef QT_KEYPAD_NAVIGATION
QBasicTimer deleteAllTimer;
#endif
diff --git a/src/widgets/widgets/qtoolbar.cpp b/src/widgets/widgets/qtoolbar.cpp
index b5950fdf23..5e5ef8e8d5 100644
--- a/src/widgets/widgets/qtoolbar.cpp
+++ b/src/widgets/widgets/qtoolbar.cpp
@@ -7,6 +7,9 @@
#if QT_CONFIG(combobox)
#include <qcombobox.h>
#endif
+#if QT_CONFIG(draganddrop)
+#include <qdrag.h>
+#endif
#include <qevent.h>
#include <qlayout.h>
#include <qmainwindow.h>
@@ -14,6 +17,7 @@
#if QT_CONFIG(menubar)
#include <qmenubar.h>
#endif
+#include <qmimedata.h>
#if QT_CONFIG(rubberband)
#include <qrubberband.h>
#endif
@@ -40,6 +44,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
// qmainwindow.cpp
extern QMainWindowLayout *qt_mainwindow_layout(const QMainWindow *window);
@@ -105,8 +111,12 @@ void QToolBarPrivate::updateWindowFlags(bool floating, bool unplug)
flags |= Qt::FramelessWindowHint;
- if (unplug)
+#if QT_CONFIG(draganddrop)
+ // If we are performing a platform drag the flag is not needed and we want to avoid recreating
+ // the platform window when it would be removed later
+ if (unplug && !QMainWindowLayout::needsPlatformDrag())
flags |= Qt::X11BypassWindowManagerHint;
+#endif
q->setWindowFlags(flags);
}
@@ -117,8 +127,6 @@ void QToolBarPrivate::setWindowState(bool floating, bool unplug, const QRect &re
bool visible = !q->isHidden();
bool wasFloating = q->isFloating(); // ...is also currently using popup menus
- q->hide();
-
updateWindowFlags(floating, unplug);
if (floating != wasFloating)
@@ -172,12 +180,29 @@ void QToolBarPrivate::startDrag(bool moving)
QMainWindowLayout *layout = qt_mainwindow_layout(win);
Q_ASSERT(layout != nullptr);
+#if QT_CONFIG(draganddrop)
+ const bool wasFloating = q->isFloating();
+#endif
+
if (!moving) {
- state->widgetItem = layout->unplug(q);
+ state->widgetItem = layout->unplug(q, QDockWidgetPrivate::DragScope::Group);
Q_ASSERT(state->widgetItem != nullptr);
}
state->dragging = !moving;
state->moving = moving;
+
+#if QT_CONFIG(draganddrop)
+ if (QMainWindowLayout::needsPlatformDrag() && state->dragging) {
+ auto result = layout->performPlatformWidgetDrag(state->widgetItem, state->pressPos);
+ if (result == Qt::IgnoreAction && !wasFloating) {
+ layout->revert(state->widgetItem);
+ delete state;
+ state = nullptr;
+ } else {
+ endDrag();
+ }
+ }
+#endif
}
void QToolBarPrivate::endDrag()
@@ -243,6 +268,13 @@ bool QToolBarPrivate::mousePressEvent(QMouseEvent *event)
bool QToolBarPrivate::mouseReleaseEvent(QMouseEvent*)
{
+#if QT_CONFIG(draganddrop)
+ // if we are peforming a platform drag ignore the release here and end the drag when the actual
+ // drag ends.
+ if (QMainWindowLayout::needsPlatformDrag())
+ return false;
+#endif
+
if (state != nullptr) {
endDrag();
return true;
@@ -293,6 +325,11 @@ bool QToolBarPrivate::mouseMoveEvent(QMouseEvent *event)
q->grabMouse();
}
+ if (!state) {
+ q->releaseMouse();
+ return true;
+ }
+
if (state->dragging) {
QPoint pos = event->globalPosition().toPoint();
// if we are right-to-left, we move so as to keep the right edge the same distance
@@ -358,6 +395,10 @@ void QToolBarPrivate::plug(const QRect &r)
\ingroup mainwindow-classes
\inmodule QtWidgets
+ A toolbar is typically created by calling
+ \l QMainWindow::addToolBar(const QString &title), but it can also
+ be added as the first widget in a QVBoxLayout, for example.
+
Toolbar buttons are added by adding \e actions, using addAction()
or insertAction(). Groups of buttons can be separated using
addSeparator() or insertSeparator(). If a toolbar button is not
@@ -381,7 +422,7 @@ void QToolBarPrivate::plug(const QRect &r)
addWidget(). Please use widget actions created by inheriting QWidgetAction
and implementing QWidgetAction::createWidget() instead.
- \sa QToolButton, QMenu, QAction, {Qt Widgets - Application Example}
+ \sa QToolButton, QMenu, QAction
*/
/*!
@@ -1043,6 +1084,7 @@ bool QToolBar::event(QEvent *event)
d->layout->setExpanded(false);
break;
}
+ break;
default:
break;
}
diff --git a/src/widgets/widgets/qtoolbarextension.cpp b/src/widgets/widgets/qtoolbarextension.cpp
index 9df3cfbda0..350a900457 100644
--- a/src/widgets/widgets/qtoolbarextension.cpp
+++ b/src/widgets/widgets/qtoolbarextension.cpp
@@ -27,9 +27,9 @@ void QToolBarExtension::setOrientation(Qt::Orientation o)
QStyleOption opt;
opt.initFrom(this);
if (o == Qt::Horizontal) {
- setIcon(style()->standardIcon(QStyle::SP_ToolBarHorizontalExtensionButton, &opt));
+ setIcon(style()->standardIcon(QStyle::SP_ToolBarHorizontalExtensionButton, &opt, this));
} else {
- setIcon(style()->standardIcon(QStyle::SP_ToolBarVerticalExtensionButton, &opt));
+ setIcon(style()->standardIcon(QStyle::SP_ToolBarVerticalExtensionButton, &opt, this));
}
m_orientation = o;
}
@@ -49,7 +49,7 @@ QSize QToolBarExtension::sizeHint() const
{
QStyleOption opt;
opt.initFrom(this);
- const int ext = style()->pixelMetric(QStyle::PM_ToolBarExtensionExtent, &opt);
+ const int ext = style()->pixelMetric(QStyle::PM_ToolBarExtensionExtent, &opt, this);
return QSize(ext, ext);
}
diff --git a/src/widgets/widgets/qtoolbutton.cpp b/src/widgets/widgets/qtoolbutton.cpp
index e6ec9ec0bc..e0775afd26 100644
--- a/src/widgets/widgets/qtoolbutton.cpp
+++ b/src/widgets/widgets/qtoolbutton.cpp
@@ -39,14 +39,14 @@ class QToolButtonPrivate : public QAbstractButtonPrivate
public:
void init();
#if QT_CONFIG(menu)
- void _q_buttonPressed();
- void _q_buttonReleased();
+ void onButtonPressed();
+ void onButtonReleased();
void popupTimerDone();
- void _q_updateButtonDown();
- void _q_menuTriggered(QAction *);
+ void updateButtonDown();
+ void onMenuTriggered(QAction *);
#endif
bool updateHoverControl(const QPoint &pos);
- void _q_actionTriggered();
+ void onActionTriggered();
QStyle::SubControl newHoverControl(const QPoint &pos);
QStyle::SubControl hoverControl;
QRect hoverRect;
@@ -187,8 +187,10 @@ void QToolButtonPrivate::init()
QSizePolicy::ToolButton));
#if QT_CONFIG(menu)
- QObject::connect(q, SIGNAL(pressed()), q, SLOT(_q_buttonPressed()));
- QObject::connect(q, SIGNAL(released()), q, SLOT(_q_buttonReleased()));
+ QObjectPrivate::connect(q, &QAbstractButton::pressed,
+ this, &QToolButtonPrivate::onButtonPressed);
+ QObjectPrivate::connect(q, &QAbstractButton::released,
+ this, &QToolButtonPrivate::onButtonReleased);
#endif
setLayoutItemMargins(QStyle::SE_ToolButtonLayoutItem);
@@ -435,7 +437,8 @@ void QToolButton::actionEvent(QActionEvent *event)
setDefaultAction(action); // update button state
break;
case QEvent::ActionAdded:
- connect(action, SIGNAL(triggered()), this, SLOT(_q_actionTriggered()));
+ QObjectPrivate::connect(action, &QAction::triggered, d,
+ &QToolButtonPrivate::onActionTriggered);
break;
case QEvent::ActionRemoved:
if (d->defaultAction == action)
@@ -480,7 +483,7 @@ bool QToolButtonPrivate::updateHoverControl(const QPoint &pos)
return !doesHover;
}
-void QToolButtonPrivate::_q_actionTriggered()
+void QToolButtonPrivate::onActionTriggered()
{
Q_Q(QToolButton);
if (QAction *action = qobject_cast<QAction *>(q->sender()))
@@ -581,8 +584,10 @@ void QToolButton::mousePressEvent(QMouseEvent *e)
void QToolButton::mouseReleaseEvent(QMouseEvent *e)
{
Q_D(QToolButton);
+ QPointer<QAbstractButton> guard(this);
QAbstractButton::mouseReleaseEvent(e);
- d->buttonPressed = QToolButtonPrivate::NoButtonPressed;
+ if (guard)
+ d->buttonPressed = QToolButtonPrivate::NoButtonPressed;
}
/*!
@@ -667,7 +672,7 @@ void QToolButton::showMenu()
d->popupTimerDone();
}
-void QToolButtonPrivate::_q_buttonPressed()
+void QToolButtonPrivate::onButtonPressed()
{
Q_Q(QToolButton);
if (!hasMenu())
@@ -680,7 +685,7 @@ void QToolButtonPrivate::_q_buttonPressed()
q->showMenu();
}
-void QToolButtonPrivate::_q_buttonReleased()
+void QToolButtonPrivate::onButtonReleased()
{
popupTimer.stop();
}
@@ -722,8 +727,13 @@ static QPoint positionMenu(const QToolButton *q, bool horizontal,
}
}
}
+
+ // QTBUG-118695 Force point inside the current screen. If the returned point
+ // is not found inside any screen, QMenu's positioning logic kicks in without
+ // taking the QToolButton's screen into account. This can cause the menu to
+ // end up on primary monitor, even if the QToolButton is on a non-primary monitor.
p.rx() = qMax(screen.left(), qMin(p.x(), screen.right() - sh.width()));
- p.ry() += 1;
+ p.ry() = qMax(screen.top(), qMin(p.y() + 1, screen.bottom()));
return p;
}
@@ -757,9 +767,12 @@ void QToolButtonPrivate::popupTimerDone()
#endif
QPointer<QToolButton> that = q;
actualMenu->setNoReplayFor(q);
- if (!mustDeleteActualMenu) //only if action are not in this widget
- QObject::connect(actualMenu, SIGNAL(triggered(QAction*)), q, SLOT(_q_menuTriggered(QAction*)));
- QObject::connect(actualMenu, SIGNAL(aboutToHide()), q, SLOT(_q_updateButtonDown()));
+ if (!mustDeleteActualMenu) { //only if action are not in this widget
+ QObjectPrivate::connect(actualMenu, &QMenu::triggered,
+ this, &QToolButtonPrivate::onMenuTriggered);
+ }
+ QObjectPrivate::connect(actualMenu, &QMenu::aboutToHide,
+ this, &QToolButtonPrivate::updateButtonDown);
actualMenu->d_func()->causedPopup.widget = q;
actualMenu->d_func()->causedPopup.action = defaultAction;
actionsCopy = q->actions(); //(the list of action may be modified in slots)
@@ -773,11 +786,14 @@ void QToolButtonPrivate::popupTimerDone()
if (!that)
return;
- QObject::disconnect(actualMenu, SIGNAL(aboutToHide()), q, SLOT(_q_updateButtonDown()));
- if (mustDeleteActualMenu)
+ QObjectPrivate::disconnect(actualMenu, &QMenu::aboutToHide,
+ this, &QToolButtonPrivate::updateButtonDown);
+ if (mustDeleteActualMenu) {
delete actualMenu;
- else
- QObject::disconnect(actualMenu, SIGNAL(triggered(QAction*)), q, SLOT(_q_menuTriggered(QAction*)));
+ } else {
+ QObjectPrivate::disconnect(actualMenu, &QMenu::triggered,
+ this, &QToolButtonPrivate::onMenuTriggered);
+ }
actionsCopy.clear();
@@ -785,7 +801,7 @@ void QToolButtonPrivate::popupTimerDone()
q->setAutoRepeat(true);
}
-void QToolButtonPrivate::_q_updateButtonDown()
+void QToolButtonPrivate::updateButtonDown()
{
Q_Q(QToolButton);
menuButtonDown = false;
@@ -795,7 +811,7 @@ void QToolButtonPrivate::_q_updateButtonDown()
q->repaint();
}
-void QToolButtonPrivate::_q_menuTriggered(QAction *action)
+void QToolButtonPrivate::onMenuTriggered(QAction *action)
{
Q_Q(QToolButton);
if (action && !actionsCopy.contains(action))
diff --git a/src/widgets/widgets/qtoolbutton.h b/src/widgets/widgets/qtoolbutton.h
index 68b04c42bf..72429620ad 100644
--- a/src/widgets/widgets/qtoolbutton.h
+++ b/src/widgets/widgets/qtoolbutton.h
@@ -89,14 +89,6 @@ protected:
private:
Q_DISABLE_COPY(QToolButton)
Q_DECLARE_PRIVATE(QToolButton)
-#if QT_CONFIG(menu)
- Q_PRIVATE_SLOT(d_func(), void _q_buttonPressed())
- Q_PRIVATE_SLOT(d_func(), void _q_buttonReleased())
- Q_PRIVATE_SLOT(d_func(), void _q_updateButtonDown())
- Q_PRIVATE_SLOT(d_func(), void _q_menuTriggered(QAction*))
-#endif
- Q_PRIVATE_SLOT(d_func(), void _q_actionTriggered())
-
};
QT_END_NAMESPACE
diff --git a/src/widgets/widgets/qwidgetanimator.cpp b/src/widgets/widgets/qwidgetanimator.cpp
index b7abd1f0dc..d45215dee3 100644
--- a/src/widgets/widgets/qwidgetanimator.cpp
+++ b/src/widgets/widgets/qwidgetanimator.cpp
@@ -73,7 +73,8 @@ void QWidgetAnimator::animate(QWidget *widget, const QRect &_final_geometry, boo
anim->setEasingCurve(QEasingCurve::InOutQuad);
anim->setEndValue(final_geometry);
m_animation_map[widget] = anim;
- connect(anim, SIGNAL(finished()), SLOT(animationFinished()));
+ connect(anim, &QPropertyAnimation::finished,
+ this, &QWidgetAnimator::animationFinished);
anim->start(QPropertyAnimation::DeleteWhenStopped);
} else
#endif // animation
diff --git a/src/widgets/widgets/qwidgetlinecontrol.cpp b/src/widgets/widgets/qwidgetlinecontrol.cpp
index 4b5d8a237f..ce1b8706ae 100644
--- a/src/widgets/widgets/qwidgetlinecontrol.cpp
+++ b/src/widgets/widgets/qwidgetlinecontrol.cpp
@@ -1638,6 +1638,7 @@ void QWidgetLineControl::processKeyEvent(QKeyEvent* event)
setText(m_completer->currentCompletion());
inlineCompletionAccepted = true;
}
+ break;
default:
break; // normal key processing
}
@@ -1809,13 +1810,18 @@ void QWidgetLineControl::processKeyEvent(QKeyEvent* event)
}
else if (event == QKeySequence::DeleteEndOfWord) {
if (!isReadOnly()) {
- cursorWordForward(true);
- del();
+ if (!hasSelectedText())
+ cursorWordForward(true);
+
+ if (hasSelectedText())
+ del();
}
}
else if (event == QKeySequence::DeleteStartOfWord) {
if (!isReadOnly()) {
- cursorWordBackward(true);
+ if (!hasSelectedText())
+ cursorWordBackward(true);
+
if (hasSelectedText())
del();
}
diff --git a/src/widgets/widgets/qwidgetlinecontrol_p.h b/src/widgets/widgets/qwidgetlinecontrol_p.h
index 838a46fff6..2ce0fe7d94 100644
--- a/src/widgets/widgets/qwidgetlinecontrol_p.h
+++ b/src/widgets/widgets/qwidgetlinecontrol_p.h
@@ -516,6 +516,8 @@ private:
// accessibility events are sent for this object
QObject *m_accessibleObject;
+
+ friend class QLineEdit;
};
QT_END_NAMESPACE
diff --git a/src/widgets/widgets/qwidgettextcontrol.cpp b/src/widgets/widgets/qwidgettextcontrol.cpp
index a4679402dc..6dcaeee0c8 100644
--- a/src/widgets/widgets/qwidgettextcontrol.cpp
+++ b/src/widgets/widgets/qwidgettextcontrol.cpp
@@ -967,9 +967,12 @@ void QWidgetTextControl::selectAll()
{
Q_D(QWidgetTextControl);
const int selectionLength = qAbs(d->cursor.position() - d->cursor.anchor());
+ const int oldCursorPos = d->cursor.position();
d->cursor.select(QTextCursor::Document);
d->selectionChanged(selectionLength != qAbs(d->cursor.position() - d->cursor.anchor()));
d->cursorIsFocusIndicator = false;
+ if (d->cursor.position() != oldCursorPos)
+ emit cursorPositionChanged();
emit updateRequest();
}
@@ -2032,6 +2035,11 @@ void QWidgetTextControlPrivate::inputMethodEvent(QInputMethodEvent *e)
|| e->preeditString() != cursor.block().layout()->preeditAreaText()
|| e->replacementLength() > 0;
+ if (!isGettingInput && e->attributes().isEmpty()) {
+ e->ignore();
+ return;
+ }
+
int oldCursorPos = cursor.position();
cursor.beginEditBlock();
@@ -2296,7 +2304,7 @@ void QWidgetTextControlPrivate::editFocusEvent(QEvent *e)
#endif
#ifndef QT_NO_CONTEXTMENU
-static inline void setActionIcon(QAction *action, const QString &name)
+void setActionIcon(QAction *action, const QString &name)
{
const QIcon icon = QIcon::fromTheme(name);
if (!icon.isNull())
@@ -2462,7 +2470,7 @@ void QWidgetTextControl::setCursorWidth(int width)
{
Q_D(QWidgetTextControl);
if (width == -1)
- width = QApplication::style()->pixelMetric(QStyle::PM_TextCursorWidth, nullptr);
+ width = QApplication::style()->pixelMetric(QStyle::PM_TextCursorWidth, nullptr, qobject_cast<QWidget *>(parent()));
d->doc->documentLayout()->setProperty("cursorWidth", width);
d->repaintCursor();
}
diff --git a/src/widgets/widgets/qwidgettextcontrol_p.h b/src/widgets/widgets/qwidgettextcontrol_p.h
index 67812dda57..9df4c0c810 100644
--- a/src/widgets/widgets/qwidgettextcontrol_p.h
+++ b/src/widgets/widgets/qwidgettextcontrol_p.h
@@ -276,6 +276,11 @@ private:
mutable QTextDocumentFragment fragment;
};
+#ifndef QT_NO_CONTEXTMENU
+// also used by QLineEdit
+void setActionIcon(QAction *action, const QString &name);
+#endif // QT_NO_CONTEXTMENU
+
QT_END_NAMESPACE
#endif // QWidgetTextControl_H