summaryrefslogtreecommitdiffstats
path: root/src/widgets/widgets/qtoolbutton.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/widgets/widgets/qtoolbutton.cpp')
-rw-r--r--src/widgets/widgets/qtoolbutton.cpp233
1 files changed, 115 insertions, 118 deletions
diff --git a/src/widgets/widgets/qtoolbutton.cpp b/src/widgets/widgets/qtoolbutton.cpp
index 6a24712319..e0775afd26 100644
--- a/src/widgets/widgets/qtoolbutton.cpp
+++ b/src/widgets/widgets/qtoolbutton.cpp
@@ -1,47 +1,9 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtWidgets module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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
#include "qtoolbutton.h"
#include <qapplication.h>
-#include <qdesktopwidget.h>
-#include <private/qdesktopwidget_p.h>
#include <qdrawutil.h>
#include <qevent.h>
#include <qicon.h>
@@ -49,7 +11,9 @@
#include <qpointer.h>
#include <qstyle.h>
#include <qstyleoption.h>
+#if QT_CONFIG(tooltip)
#include <qtooltip.h>
+#endif
#if QT_CONFIG(mainwindow)
#include <qmainwindow.h>
#endif
@@ -67,20 +31,22 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
class QToolButtonPrivate : public QAbstractButtonPrivate
{
Q_DECLARE_PUBLIC(QToolButton)
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;
@@ -169,8 +135,7 @@ bool QToolButtonPrivate::hasMenu() const
with actions used in other parts of the main window.
\endtable
- \sa QPushButton, QToolBar, QMainWindow, QAction,
- {fowler}{GUI Design Handbook: Push Button}
+ \sa QPushButton, QToolBar, QMainWindow, QAction
*/
/*!
@@ -202,7 +167,7 @@ QToolButton::QToolButton(QWidget * parent)
void QToolButtonPrivate::init()
{
Q_Q(QToolButton);
- defaultAction = 0;
+ defaultAction = nullptr;
#if QT_CONFIG(toolbar)
if (qobject_cast<QToolBar*>(parent))
autoRaise = true;
@@ -222,12 +187,14 @@ 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);
- delay = q->style()->styleHint(QStyle::SH_ToolButton_PopupDelay, 0, q);
+ delay = q->style()->styleHint(QStyle::SH_ToolButton_PopupDelay, nullptr, q);
}
/*!
@@ -244,7 +211,6 @@ void QToolButton::initStyleOption(QStyleOptionToolButton *option) const
Q_D(const QToolButton);
option->initFrom(this);
- bool forceNoText = false;
option->iconSize = iconSize(); //default value
#if QT_CONFIG(toolbar)
@@ -255,8 +221,7 @@ void QToolButton::initStyleOption(QStyleOptionToolButton *option) const
}
#endif // QT_CONFIG(toolbar)
- if (!forceNoText)
- option->text = d->text;
+ option->text = d->text;
option->icon = d->icon;
option->arrowType = d->arrowType;
if (d->down)
@@ -308,7 +273,7 @@ void QToolButton::initStyleOption(QStyleOptionToolButton *option) const
option->toolButtonStyle = Qt::ToolButtonIconOnly;
}
- if (d->icon.isNull() && d->arrowType == Qt::NoArrow && !forceNoText) {
+ if (d->icon.isNull() && d->arrowType == Qt::NoArrow) {
if (!d->text.isEmpty())
option->toolButtonStyle = Qt::ToolButtonTextOnly;
else if (option->toolButtonStyle != Qt::ToolButtonTextOnly)
@@ -350,7 +315,7 @@ QSize QToolButton::sizeHint() const
if (opt.toolButtonStyle != Qt::ToolButtonIconOnly) {
QSize textSize = fm.size(Qt::TextShowMnemonic, text());
- textSize.setWidth(textSize.width() + fm.horizontalAdvance(QLatin1Char(' '))*2);
+ textSize.setWidth(textSize.width() + fm.horizontalAdvance(u' ') * 2);
if (opt.toolButtonStyle == Qt::ToolButtonTextUnderIcon) {
h += 4 + textSize.height();
if (textSize.width() > w)
@@ -369,8 +334,7 @@ QSize QToolButton::sizeHint() const
if (d->popupMode == MenuButtonPopup)
w += style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt, this);
- d->sizeHint = style()->sizeFromContents(QStyle::CT_ToolButton, &opt, QSize(w, h), this).
- expandedTo(QApplication::globalStrut());
+ d->sizeHint = style()->sizeFromContents(QStyle::CT_ToolButton, &opt, QSize(w, h), this);
return d->sizeHint;
}
@@ -466,21 +430,22 @@ void QToolButton::paintEvent(QPaintEvent *)
void QToolButton::actionEvent(QActionEvent *event)
{
Q_D(QToolButton);
- QAction *action = event->action();
+ auto action = static_cast<QAction *>(event->action());
switch (event->type()) {
case QEvent::ActionChanged:
if (action == d->defaultAction)
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)
- d->defaultAction = 0;
+ d->defaultAction = nullptr;
#if QT_CONFIG(menu)
if (action == d->menuAction)
- d->menuAction = 0;
+ d->menuAction = nullptr;
#endif
action->disconnect(this);
break;
@@ -518,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()))
@@ -528,7 +493,7 @@ void QToolButtonPrivate::_q_actionTriggered()
/*!
\reimp
*/
-void QToolButton::enterEvent(QEvent * e)
+void QToolButton::enterEvent(QEnterEvent * e)
{
Q_D(QToolButton);
if (d->autoRaise)
@@ -583,7 +548,7 @@ void QToolButton::changeEvent(QEvent *e)
|| e->type() == QEvent::MacSizeChange
#endif
) {
- d->delay = style()->styleHint(QStyle::SH_ToolButton_PopupDelay, 0, this);
+ d->delay = style()->styleHint(QStyle::SH_ToolButton_PopupDelay, nullptr, this);
d->setLayoutItemMargins(QStyle::SE_ToolButtonLayoutItem);
}
#endif
@@ -602,7 +567,7 @@ void QToolButton::mousePressEvent(QMouseEvent *e)
if (e->button() == Qt::LeftButton && (d->popupMode == MenuButtonPopup)) {
QRect popupr = style()->subControlRect(QStyle::CC_ToolButton, &opt,
QStyle::SC_ToolButtonMenu, this);
- if (popupr.isValid() && popupr.contains(e->pos())) {
+ if (popupr.isValid() && popupr.contains(e->position().toPoint())) {
d->buttonPressed = QToolButtonPrivate::MenuButtonPressed;
showMenu();
return;
@@ -619,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;
}
/*!
@@ -629,7 +596,7 @@ void QToolButton::mouseReleaseEvent(QMouseEvent *e)
bool QToolButton::hitButton(const QPoint &pos) const
{
Q_D(const QToolButton);
- if(QAbstractButton::hitButton(pos))
+ if (QAbstractButton::hitButton(pos))
return (d->buttonPressed != QToolButtonPrivate::MenuButtonPressed);
return false;
}
@@ -649,7 +616,7 @@ void QToolButton::setMenu(QMenu* menu)
{
Q_D(QToolButton);
- if (d->menuAction == (menu ? menu->menuAction() : 0))
+ if (d->menuAction == (menu ? menu->menuAction() : nullptr))
return;
if (d->menuAction)
@@ -659,7 +626,7 @@ void QToolButton::setMenu(QMenu* menu)
d->menuAction = menu->menuAction();
addAction(d->menuAction);
} else {
- d->menuAction = 0;
+ d->menuAction = nullptr;
}
// changing the menu set may change the size hint, so reset it
@@ -669,7 +636,8 @@ void QToolButton::setMenu(QMenu* menu)
}
/*!
- Returns the associated menu, or 0 if no menu has been defined.
+ Returns the associated menu, or \nullptr if no menu has been
+ defined.
\sa setMenu()
*/
@@ -678,7 +646,7 @@ QMenu* QToolButton::menu() const
Q_D(const QToolButton);
if (d->menuAction)
return d->menuAction->menu();
- return 0;
+ return nullptr;
}
/*!
@@ -704,7 +672,7 @@ void QToolButton::showMenu()
d->popupTimerDone();
}
-void QToolButtonPrivate::_q_buttonPressed()
+void QToolButtonPrivate::onButtonPressed()
{
Q_Q(QToolButton);
if (!hasMenu())
@@ -717,43 +685,17 @@ void QToolButtonPrivate::_q_buttonPressed()
q->showMenu();
}
-void QToolButtonPrivate::_q_buttonReleased()
+void QToolButtonPrivate::onButtonReleased()
{
popupTimer.stop();
}
-void QToolButtonPrivate::popupTimerDone()
+static QPoint positionMenu(const QToolButton *q, bool horizontal,
+ const QSize &sh)
{
- Q_Q(QToolButton);
- popupTimer.stop();
- if (!menuButtonDown && !down)
- return;
-
- menuButtonDown = true;
- QPointer<QMenu> actualMenu;
- bool mustDeleteActualMenu = false;
- if(menuAction) {
- actualMenu = menuAction->menu();
- } else if (defaultAction && defaultAction->menu()) {
- actualMenu = defaultAction->menu();
- } else {
- actualMenu = new QMenu(q);
- mustDeleteActualMenu = true;
- for(int i = 0; i < actions.size(); i++)
- actualMenu->addAction(actions.at(i));
- }
- repeat = q->autoRepeat();
- q->setAutoRepeat(false);
- bool horizontal = true;
-#if QT_CONFIG(toolbar)
- QToolBar *tb = qobject_cast<QToolBar*>(parent);
- if (tb && tb->orientation() == Qt::Vertical)
- horizontal = false;
-#endif
QPoint p;
const QRect rect = q->rect(); // Find screen via point in case of QGraphicsProxyWidget.
- QRect screen = QDesktopWidgetPrivate::availableGeometry(q->mapToGlobal(rect.center()));
- QSize sh = ((QToolButton*)(QMenu*)actualMenu)->receivers(SIGNAL(aboutToShow()))? QSize() : actualMenu->sizeHint();
+ const QRect screen = QWidgetPrivate::availableScreenGeometry(q, q->mapToGlobal(rect.center()));
if (horizontal) {
if (q->isRightToLeft()) {
if (q->mapToGlobal(QPoint(0, rect.bottom())).y() + sh.height() <= screen.bottom()) {
@@ -785,26 +727,73 @@ void QToolButtonPrivate::popupTimerDone()
}
}
}
+
+ // 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;
+}
+
+void QToolButtonPrivate::popupTimerDone()
+{
+ Q_Q(QToolButton);
+ popupTimer.stop();
+ if (!menuButtonDown && !down)
+ return;
+
+ menuButtonDown = true;
+ QPointer<QMenu> actualMenu;
+ bool mustDeleteActualMenu = false;
+ if (menuAction) {
+ actualMenu = menuAction->menu();
+ } else if (defaultAction && defaultAction->menu()) {
+ actualMenu = defaultAction->menu();
+ } else {
+ actualMenu = new QMenu(q);
+ mustDeleteActualMenu = true;
+ for (int i = 0; i < actions.size(); i++)
+ actualMenu->addAction(actions.at(i));
+ }
+ repeat = q->autoRepeat();
+ q->setAutoRepeat(false);
+ bool horizontal = true;
+#if QT_CONFIG(toolbar)
+ QToolBar *tb = qobject_cast<QToolBar*>(parent);
+ if (tb && tb->orientation() == Qt::Vertical)
+ horizontal = false;
+#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)
- actualMenu->exec(p);
+
+ // QTBUG-78966, Delay positioning until after aboutToShow().
+ auto positionFunction = [q, horizontal](const QSize &sizeHint) {
+ return positionMenu(q, horizontal, sizeHint); };
+ const auto initialPos = positionFunction(actualMenu->sizeHint());
+ actualMenu->d_func()->exec(initialPos, nullptr, positionFunction);
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();
@@ -812,7 +801,7 @@ void QToolButtonPrivate::popupTimerDone()
q->setAutoRepeat(true);
}
-void QToolButtonPrivate::_q_updateButtonDown()
+void QToolButtonPrivate::updateButtonDown()
{
Q_Q(QToolButton);
menuButtonDown = false;
@@ -822,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))
@@ -932,10 +921,10 @@ void QToolButton::setDefaultAction(QAction *action)
// If iconText() is generated from text(), we need to escape any '&'s so they
// don't turn into shortcuts
if (QActionPrivate::get(action)->iconText.isEmpty())
- buttonText.replace(QLatin1String("&"), QLatin1String("&&"));
+ buttonText.replace("&"_L1, "&&"_L1);
setText(buttonText);
setIcon(action->icon());
-#ifndef QT_NO_TOOLTIP
+#if QT_CONFIG(tooltip)
setToolTip(action->toolTip());
#endif
#if QT_CONFIG(statustip)
@@ -971,7 +960,15 @@ QAction *QToolButton::defaultAction() const
return d->defaultAction;
}
-
+/*!
+ \reimp
+ */
+void QToolButton::checkStateSet()
+{
+ Q_D(QToolButton);
+ if (d->defaultAction && d->defaultAction->isCheckable())
+ d->defaultAction->setChecked(isChecked());
+}
/*!
\reimp
@@ -993,7 +990,7 @@ bool QToolButton::event(QEvent *event)
case QEvent::HoverLeave:
case QEvent::HoverMove:
if (const QHoverEvent *he = static_cast<const QHoverEvent *>(event))
- d_func()->updateHoverControl(he->pos());
+ d_func()->updateHoverControl(he->position().toPoint());
break;
default:
break;