diff options
Diffstat (limited to 'src/widgets/widgets/qtoolbutton.cpp')
-rw-r--r-- | src/widgets/widgets/qtoolbutton.cpp | 233 |
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; |