Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qtoolbutton.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include "qtoolbutton.h"
6
7#include <qapplication.h>
8#include <qdrawutil.h>
9#include <qevent.h>
10#include <qicon.h>
11#include <qpainter.h>
12#include <qpointer.h>
13#include <qstyle.h>
14#include <qstyleoption.h>
15#if QT_CONFIG(tooltip)
16#include <qtooltip.h>
17#endif
18#if QT_CONFIG(mainwindow)
19#include <qmainwindow.h>
20#endif
21#if QT_CONFIG(toolbar)
22#include <qtoolbar.h>
23#endif
24#include <qvariant.h>
25#include <qstylepainter.h>
26#include <private/qabstractbutton_p.h>
27#include <private/qaction_p.h>
28#if QT_CONFIG(menu)
29#include <qmenu.h>
30#include <private/qmenu_p.h>
31#endif
32
34
35using namespace Qt::StringLiterals;
36
38{
39 Q_DECLARE_PUBLIC(QToolButton)
40public:
41 void init();
42#if QT_CONFIG(menu)
43 void onButtonPressed();
44 void onButtonReleased();
45 void popupTimerDone();
46 void updateButtonDown();
49#endif
50 bool updateHoverControl(const QPoint &pos);
52 QStyle::State styleButtonState(QStyle::State state) const override;
55 QRect hoverRect;
56 QPointer<QAction> menuAction; //the menu set by the user (setMenu)
58 int delay;
62 uint popupModeSetByUser : 1; // true if popupMode was set through setPopupMode
68 QAction *defaultAction;
69#if QT_CONFIG(menu)
70 bool hasMenu() const;
71 //workaround for task 177850
73#endif
74};
75
76#if QT_CONFIG(menu)
77bool QToolButtonPrivate::hasMenu() const
78{
79 return ((defaultAction && defaultAction->menu())
80 || (menuAction && menuAction->menu())
81 || actions.size() > (defaultAction ? 1 : 0));
82}
83#endif
84
85/*!
86 \class QToolButton
87 \brief The QToolButton class provides a quick-access button to
88 commands or options, usually used inside a QToolBar.
89
90 \ingroup basicwidgets
91 \inmodule QtWidgets
92
93 A tool button is a special button that provides quick-access to
94 specific commands or options. As opposed to a normal command
95 button, a tool button usually doesn't show a text label, but shows
96 an icon instead.
97
98 Tool buttons are normally created when new QAction instances are
99 created with QToolBar::addAction() or existing actions are added
100 to a toolbar with QToolBar::addAction(). It is also possible to
101 construct tool buttons in the same way as any other widget, and
102 arrange them alongside other widgets in layouts.
103
104 One classic use of a tool button is to select tools; for example,
105 the "pen" tool in a drawing program. This would be implemented
106 by using a QToolButton as a toggle button (see setCheckable()).
107
108 QToolButton supports auto-raising. In auto-raise mode, the button
109 draws a 3D frame only when the mouse points at it. The feature is
110 automatically turned on when a button is used inside a QToolBar.
111 Change it with setAutoRaise().
112
113 A tool button's icon is set as QIcon. This makes it possible to
114 specify different pixmaps for the disabled and active state. The
115 disabled pixmap is used when the button's functionality is not
116 available. The active pixmap is displayed when the button is
117 auto-raised because the mouse pointer is hovering over it.
118
119 The button's look and dimension is adjustable with
120 setToolButtonStyle() and setIconSize(). When used inside a
121 QToolBar in a QMainWindow, the button automatically adjusts to
122 QMainWindow's settings (see QMainWindow::setToolButtonStyle() and
123 QMainWindow::setIconSize()). Instead of an icon, a tool button can
124 also display an arrow symbol, specified with
125 \l{QToolButton::arrowType} {arrowType}.
126
127 A tool button can offer additional choices in a popup menu. The
128 popup menu can be set using setMenu(). Use setPopupMode() to
129 configure the different modes available for tool buttons with a
130 menu set. The default mode is DelayedPopupMode which is sometimes
131 used with the "Back" button in a web browser. After pressing and
132 holding the button down for a while, a menu pops up showing a list
133 of possible pages to jump to. The timeout is style dependent,
134 see QStyle::SH_ToolButton_PopupDelay.
135
136 \image assistant-toolbar.png {Qt Assistant's toolbar with tool buttons}
137 \caption Qt Assistant's toolbar contains tool buttons that are associated
138 with actions used in other parts of the main window.
139
140
141 \sa QPushButton, QToolBar, QMainWindow, QAction
142*/
143
144/*!
145 \fn void QToolButton::triggered(QAction *action)
146
147 This signal is emitted when the given \a action is triggered.
148
149 The action may also be associated with other parts of the user interface,
150 such as menu items and keyboard shortcuts. Sharing actions in this
151 way helps make the user interface more consistent and is often less work
152 to implement.
153*/
154
155/*!
156 Constructs an empty tool button with parent \a
157 parent.
158*/
159QToolButton::QToolButton(QWidget * parent)
160 : QAbstractButton(*new QToolButtonPrivate, parent)
161{
162 Q_D(QToolButton);
163 d->init();
164}
165
166
167
168/* Set-up code common to all the constructors */
169
171{
172 Q_Q(QToolButton);
173 defaultAction = nullptr;
174#if QT_CONFIG(toolbar)
175 if (qobject_cast<QToolBar*>(parent))
176 autoRaise = true;
177 else
178#endif
179 autoRaise = false;
180 arrowType = Qt::NoArrow;
181 menuButtonDown = false;
182 popupMode = QToolButton::DelayedPopup;
183 popupModeSetByUser = false;
184 buttonPressed = QToolButtonPrivate::NoButtonPressed;
185
186 toolButtonStyle = Qt::ToolButtonIconOnly;
187 hoverControl = QStyle::SC_None;
188
189 q->setFocusPolicy(Qt::TabFocus);
190 q->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed,
191 QSizePolicy::ToolButton));
192
193#if QT_CONFIG(menu)
194 QObjectPrivate::connect(q, &QAbstractButton::pressed,
195 this, &QToolButtonPrivate::onButtonPressed);
196 QObjectPrivate::connect(q, &QAbstractButton::released,
197 this, &QToolButtonPrivate::onButtonReleased);
198#endif
199
200 setLayoutItemMargins(QStyle::SE_ToolButtonLayoutItem);
201 delay = q->style()->styleHint(QStyle::SH_ToolButton_PopupDelay, nullptr, q);
202}
203
204/*!
205 Initialize \a option with the values from this QToolButton. This method
206 is useful for subclasses when they need a QStyleOptionToolButton, but don't want
207 to fill in all the information themselves.
208
209 \sa QStyleOption::initFrom()
210*/
211void QToolButton::initStyleOption(QStyleOptionToolButton *option) const
212{
213 if (!option)
214 return;
215
216 Q_D(const QToolButton);
217 option->initFrom(this);
218 option->iconSize = iconSize(); //default value
219
220#if QT_CONFIG(toolbar)
221 if (parentWidget()) {
222 if (QToolBar *toolBar = qobject_cast<QToolBar *>(parentWidget())) {
223 option->iconSize = toolBar->iconSize();
224 }
225 }
226#endif // QT_CONFIG(toolbar)
227
228 option->text = d->text;
229 option->icon = d->icon;
230 option->arrowType = d->arrowType;
231 option->state = d->styleButtonState(option->state);
232 option->subControls = QStyle::SC_ToolButton;
233 option->activeSubControls = QStyle::SC_None;
234
235 option->features = QStyleOptionToolButton::None;
236 if (d->popupMode == QToolButton::MenuButtonPopup) {
237 option->subControls |= QStyle::SC_ToolButtonMenu;
238 option->features |= QStyleOptionToolButton::MenuButtonPopup;
239 }
240 if (option->state & QStyle::State_MouseOver) {
241 option->activeSubControls = d->hoverControl;
242 }
243 if (d->menuButtonDown) {
244 option->state |= QStyle::State_Sunken;
245 option->activeSubControls |= QStyle::SC_ToolButtonMenu;
246 }
247 if (d->down) {
248 option->state |= QStyle::State_Sunken;
249 option->activeSubControls |= QStyle::SC_ToolButton;
250 }
251
252
253 if (d->arrowType != Qt::NoArrow)
254 option->features |= QStyleOptionToolButton::Arrow;
255 if (d->popupMode == QToolButton::DelayedPopup)
256 option->features |= QStyleOptionToolButton::PopupDelay;
257#if QT_CONFIG(menu)
258 if (d->hasMenu())
259 option->features |= QStyleOptionToolButton::HasMenu;
260#endif
261 if (d->toolButtonStyle == Qt::ToolButtonFollowStyle) {
262 option->toolButtonStyle = Qt::ToolButtonStyle(style()->styleHint(QStyle::SH_ToolButtonStyle, option, this));
263 } else
264 option->toolButtonStyle = d->toolButtonStyle;
265
266 if (option->toolButtonStyle == Qt::ToolButtonTextBesideIcon) {
267 // If the action is not prioritized, remove the text label to save space
268 if (d->defaultAction && d->defaultAction->priority() < QAction::NormalPriority)
269 option->toolButtonStyle = Qt::ToolButtonIconOnly;
270 }
271
272 if (d->icon.isNull() && d->arrowType == Qt::NoArrow) {
273 if (!d->text.isEmpty())
274 option->toolButtonStyle = Qt::ToolButtonTextOnly;
275 else if (option->toolButtonStyle != Qt::ToolButtonTextOnly)
276 option->toolButtonStyle = Qt::ToolButtonIconOnly;
277 }
278
279 option->pos = pos();
280 option->font = font();
281}
282
283/*!
284 Destroys the object and frees any allocated resources.
285*/
286
287QToolButton::~QToolButton()
288{
289}
290
291/*!
292 \reimp
293*/
294QSize QToolButton::sizeHint() const
295{
296 Q_D(const QToolButton);
297 if (d->sizeHint.isValid())
298 return d->sizeHint;
299 ensurePolished();
300
301 int w = 0, h = 0;
302 QStyleOptionToolButton opt;
303 initStyleOption(&opt);
304
305 QFontMetrics fm = fontMetrics();
306 if (opt.toolButtonStyle != Qt::ToolButtonTextOnly) {
307 QSize icon = opt.iconSize;
308 w = icon.width();
309 h = icon.height();
310 }
311
312 if (opt.toolButtonStyle != Qt::ToolButtonIconOnly) {
313 QSize textSize = fm.size(Qt::TextShowMnemonic, text());
314 textSize.setWidth(textSize.width() + fm.horizontalAdvance(u' ') * 2);
315 if (opt.toolButtonStyle == Qt::ToolButtonTextUnderIcon) {
316 h += 4 + textSize.height();
317 if (textSize.width() > w)
318 w = textSize.width();
319 } else if (opt.toolButtonStyle == Qt::ToolButtonTextBesideIcon) {
320 w += 4 + textSize.width();
321 if (textSize.height() > h)
322 h = textSize.height();
323 } else { // TextOnly
324 w = textSize.width();
325 h = textSize.height();
326 }
327 }
328
329 opt.rect.setSize(QSize(w, h)); // PM_MenuButtonIndicator depends on the height
330 if (d->popupMode == MenuButtonPopup)
331 w += style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt, this);
332
333 d->sizeHint = style()->sizeFromContents(QStyle::CT_ToolButton, &opt, QSize(w, h), this);
334 return d->sizeHint;
335}
336
337/*!
338 \reimp
339 */
340QSize QToolButton::minimumSizeHint() const
341{
342 return sizeHint();
343}
344
345/*!
346 \property QToolButton::toolButtonStyle
347 \brief whether the tool button displays an icon only, text only,
348 or text beside/below the icon.
349
350 The default is Qt::ToolButtonIconOnly.
351
352 To have the style of toolbuttons follow the system settings, set this property to Qt::ToolButtonFollowStyle.
353 On Unix, the user settings from the desktop environment will be used.
354 On other platforms, Qt::ToolButtonFollowStyle means icon only.
355
356 QToolButton automatically connects this slot to the relevant
357 signal in the QMainWindow in which is resides.
358*/
359
360/*!
361 \property QToolButton::arrowType
362 \brief whether the button displays an arrow instead of a normal icon
363
364 This displays an arrow as the icon for the QToolButton.
365
366 By default, this property is set to Qt::NoArrow.
367*/
368
369Qt::ToolButtonStyle QToolButton::toolButtonStyle() const
370{
371 Q_D(const QToolButton);
372 return d->toolButtonStyle;
373}
374
375Qt::ArrowType QToolButton::arrowType() const
376{
377 Q_D(const QToolButton);
378 return d->arrowType;
379}
380
381
382void QToolButton::setToolButtonStyle(Qt::ToolButtonStyle style)
383{
384 Q_D(QToolButton);
385 if (d->toolButtonStyle == style)
386 return;
387
388 d->toolButtonStyle = style;
389 d->sizeHint = QSize();
390 updateGeometry();
391 if (isVisible()) {
392 update();
393 }
394}
395
396void QToolButton::setArrowType(Qt::ArrowType type)
397{
398 Q_D(QToolButton);
399 if (d->arrowType == type)
400 return;
401
402 d->arrowType = type;
403 d->sizeHint = QSize();
404 updateGeometry();
405 if (isVisible()) {
406 update();
407 }
408}
409
410/*!
411 \fn void QToolButton::paintEvent(QPaintEvent *event)
412
413 Paints the button in response to the paint \a event.
414*/
415void QToolButton::paintEvent(QPaintEvent *)
416{
417 QStylePainter p(this);
418 QStyleOptionToolButton opt;
419 initStyleOption(&opt);
420 p.drawComplexControl(QStyle::CC_ToolButton, opt);
421}
422
423/*!
424 \reimp
425 */
426void QToolButton::actionEvent(QActionEvent *event)
427{
428 Q_D(QToolButton);
429 auto action = static_cast<QAction *>(event->action());
430 switch (event->type()) {
431 case QEvent::ActionChanged:
432 if (action == d->defaultAction)
433 setDefaultAction(action); // update button state
434 break;
435 case QEvent::ActionAdded:
436 QObjectPrivate::connect(action, &QAction::triggered, d,
437 &QToolButtonPrivate::onActionTriggered);
438 break;
439 case QEvent::ActionRemoved:
440 if (d->defaultAction == action)
441 d->defaultAction = nullptr;
442#if QT_CONFIG(menu)
443 if (action == d->menuAction)
444 d->menuAction = nullptr;
445#endif
446 action->disconnect(this);
447 break;
448 default:
449 ;
450 }
451 QAbstractButton::actionEvent(event);
452}
453
455{
456 Q_Q(QToolButton);
457 QStyleOptionToolButton opt;
458 q->initStyleOption(&opt);
459 opt.subControls = QStyle::SC_All;
460 hoverControl = q->style()->hitTestComplexControl(QStyle::CC_ToolButton, &opt, pos, q);
461 if (hoverControl == QStyle::SC_None)
462 hoverRect = QRect();
463 else
464 hoverRect = q->style()->subControlRect(QStyle::CC_ToolButton, &opt, hoverControl, q);
465 return hoverControl;
466}
467
469{
470 Q_Q(QToolButton);
471 QRect lastHoverRect = hoverRect;
472 QStyle::SubControl lastHoverControl = hoverControl;
473 bool doesHover = q->testAttribute(Qt::WA_Hover);
474 if (lastHoverControl != newHoverControl(pos) && doesHover) {
475 q->update(lastHoverRect);
476 q->update(hoverRect);
477 return true;
478 }
479 return !doesHover;
480}
481
483{
484 Q_Q(QToolButton);
485 if (QAction *action = qobject_cast<QAction *>(q->sender()))
486 emit q->triggered(action);
487}
488
490{
491 state = QAbstractButtonPrivate::styleButtonState(state);
492 if (checked)
493 state |= QStyle::State_On;
494 if (autoRaise)
495 state |= QStyle::State_AutoRaise;
496 if (!checked && !down)
497 state |= QStyle::State_Raised;
498 if (menuButtonDown)
499 state |= QStyle::State_Sunken;
500 return state;
501}
502
503/*!
504 \reimp
505 */
506void QToolButton::enterEvent(QEnterEvent * e)
507{
508 Q_D(QToolButton);
509 if (d->autoRaise)
510 update();
511 if (d->defaultAction)
512 d->defaultAction->hover();
513 QAbstractButton::enterEvent(e);
514}
515
516
517/*!
518 \reimp
519 */
520void QToolButton::leaveEvent(QEvent * e)
521{
522 Q_D(QToolButton);
523 if (d->autoRaise)
524 update();
525
526 QAbstractButton::leaveEvent(e);
527}
528
529
530/*!
531 \reimp
532 */
533void QToolButton::timerEvent(QTimerEvent *e)
534{
535#if QT_CONFIG(menu)
536 Q_D(QToolButton);
537 if (e->timerId() == d->popupTimer.timerId()) {
538 d->popupTimerDone();
539 return;
540 }
541#endif
542 QAbstractButton::timerEvent(e);
543}
544
545
546/*!
547 \reimp
548*/
549void QToolButton::changeEvent(QEvent *e)
550{
551#if QT_CONFIG(toolbar)
552 Q_D(QToolButton);
553 if (e->type() == QEvent::ParentChange) {
554 if (qobject_cast<QToolBar*>(parentWidget()))
555 d->autoRaise = true;
556 } else if (e->type() == QEvent::StyleChange
557#ifdef Q_OS_MAC
558 || e->type() == QEvent::MacSizeChange
559#endif
560 ) {
561 d->delay = style()->styleHint(QStyle::SH_ToolButton_PopupDelay, nullptr, this);
562 d->setLayoutItemMargins(QStyle::SE_ToolButtonLayoutItem);
563 }
564#endif
565 QAbstractButton::changeEvent(e);
566}
567
568/*!
569 \reimp
570*/
571void QToolButton::mousePressEvent(QMouseEvent *e)
572{
573 Q_D(QToolButton);
574#if QT_CONFIG(menu)
575 QStyleOptionToolButton opt;
576 initStyleOption(&opt);
577 if (e->button() == Qt::LeftButton && (d->popupMode == MenuButtonPopup)) {
578 QRect popupr = style()->subControlRect(QStyle::CC_ToolButton, &opt,
579 QStyle::SC_ToolButtonMenu, this);
580 if (popupr.isValid() && popupr.contains(e->position().toPoint())) {
581 d->buttonPressed = QToolButtonPrivate::MenuButtonPressed;
582 showMenu();
583 return;
584 }
585 }
586#endif
587 d->buttonPressed = QToolButtonPrivate::ToolButtonPressed;
588 QAbstractButton::mousePressEvent(e);
589}
590
591/*!
592 \reimp
593*/
594void QToolButton::mouseReleaseEvent(QMouseEvent *e)
595{
596 Q_D(QToolButton);
597 QPointer<QAbstractButton> guard(this);
598 QAbstractButton::mouseReleaseEvent(e);
599 if (guard)
600 d->buttonPressed = QToolButtonPrivate::NoButtonPressed;
601}
602
603/*!
604 \reimp
605*/
606bool QToolButton::hitButton(const QPoint &pos) const
607{
608 Q_D(const QToolButton);
609 if (QAbstractButton::hitButton(pos))
610 return (d->buttonPressed != QToolButtonPrivate::MenuButtonPressed);
611 return false;
612}
613
614
615#if QT_CONFIG(menu)
616/*!
617 Associates the given \a menu with this tool button.
618
619 The menu will be shown according to the button's \l popupMode.
620
621 Ownership of the menu is not transferred to the tool button.
622
623 \sa menu()
624*/
625void QToolButton::setMenu(QMenu* menu)
626{
627 Q_D(QToolButton);
628
629 if (d->menuAction == (menu ? menu->menuAction() : nullptr))
630 return;
631
632 if (d->menuAction)
633 removeAction(d->menuAction);
634
635 if (menu) {
636 d->menuAction = menu->menuAction();
637 addAction(d->menuAction);
638 } else {
639 d->menuAction = nullptr;
640 }
641
642 // changing the menu set may change the size hint, so reset it
643 d->sizeHint = QSize();
644 updateGeometry();
645 update();
646}
647
648/*!
649 Returns the associated menu, or \nullptr if no menu has been
650 defined.
651
652 \sa setMenu()
653*/
654QMenu* QToolButton::menu() const
655{
656 Q_D(const QToolButton);
657 if (d->menuAction)
658 return d->menuAction->menu();
659 return nullptr;
660}
661
662/*!
663 Shows (pops up) the associated popup menu. If there is no such
664 menu, this function does nothing. This function does not return
665 until the popup menu has been closed by the user.
666*/
667void QToolButton::showMenu()
668{
669 Q_D(QToolButton);
670 if (!d->hasMenu()) {
671 d->menuButtonDown = false;
672 return; // no menu to show
673 }
674 // prevent recursions spinning another event loop
675 if (d->menuButtonDown)
676 return;
677
678
679 d->menuButtonDown = true;
680 repaint();
681 d->popupTimer.stop();
682 d->popupTimerDone();
683}
684
685void QToolButtonPrivate::onButtonPressed()
686{
687 Q_Q(QToolButton);
688 if (!hasMenu())
689 return; // no menu to show
690 if (popupMode == QToolButton::MenuButtonPopup)
691 return;
692 else if (delay > 0 && popupMode == QToolButton::DelayedPopup)
693 popupTimer.start(delay, q);
694 else if (delay == 0 || popupMode == QToolButton::InstantPopup)
695 q->showMenu();
696}
697
698void QToolButtonPrivate::onButtonReleased()
699{
700 popupTimer.stop();
701}
702
703static QPoint positionMenu(const QToolButton *q, bool horizontal,
704 const QSize &sh)
705{
706 QPoint p;
707 const QRect rect = q->rect(); // Find screen via point in case of QGraphicsProxyWidget.
708 const QRect screen = QWidgetPrivate::availableScreenGeometry(q, q->mapToGlobal(rect.center()));
709 if (horizontal) {
710 if (q->isRightToLeft()) {
711 if (q->mapToGlobal(QPoint(0, rect.bottom())).y() + sh.height() <= screen.bottom()) {
712 p = q->mapToGlobal(rect.bottomRight());
713 } else {
714 p = q->mapToGlobal(rect.topRight() - QPoint(0, sh.height()));
715 }
716 p.rx() -= sh.width();
717 } else {
718 if (q->mapToGlobal(QPoint(0, rect.bottom())).y() + sh.height() <= screen.bottom()) {
719 p = q->mapToGlobal(rect.bottomLeft());
720 } else {
721 p = q->mapToGlobal(rect.topLeft() - QPoint(0, sh.height()));
722 }
723 }
724 } else {
725 if (q->isRightToLeft()) {
726 if (q->mapToGlobal(QPoint(rect.left(), 0)).x() - sh.width() <= screen.x()) {
727 p = q->mapToGlobal(rect.topRight());
728 } else {
729 p = q->mapToGlobal(rect.topLeft());
730 p.rx() -= sh.width();
731 }
732 } else {
733 if (q->mapToGlobal(QPoint(rect.right(), 0)).x() + sh.width() <= screen.right()) {
734 p = q->mapToGlobal(rect.topRight());
735 } else {
736 p = q->mapToGlobal(rect.topLeft() - QPoint(sh.width(), 0));
737 }
738 }
739 }
740
741 // QTBUG-118695 Force point inside the current screen. If the returned point
742 // is not found inside any screen, QMenu's positioning logic kicks in without
743 // taking the QToolButton's screen into account. This can cause the menu to
744 // end up on primary monitor, even if the QToolButton is on a non-primary monitor.
745 p.rx() = qMax(screen.left(), qMin(p.x(), screen.right() - sh.width()));
746 p.ry() = qMax(screen.top(), qMin(p.y() + 1, screen.bottom()));
747 return p;
748}
749
750void QToolButtonPrivate::popupTimerDone()
751{
752 Q_Q(QToolButton);
753 popupTimer.stop();
754 if (!menuButtonDown && !down)
755 return;
756
757 menuButtonDown = true;
758 QPointer<QMenu> actualMenu;
759 bool mustDeleteActualMenu = false;
760 if (menuAction) {
761 actualMenu = menuAction->menu();
762 } else if (defaultAction && defaultAction->menu()) {
763 actualMenu = defaultAction->menu();
764 } else {
765 actualMenu = new QMenu(q);
766 mustDeleteActualMenu = true;
767 for (int i = 0; i < actions.size(); i++)
768 actualMenu->addAction(actions.at(i));
769 }
770 repeat = q->autoRepeat();
771 q->setAutoRepeat(false);
772 bool horizontal = true;
773#if QT_CONFIG(toolbar)
774 QToolBar *tb = qobject_cast<QToolBar*>(parent);
775 if (tb && tb->orientation() == Qt::Vertical)
776 horizontal = false;
777#endif
778 QPointer<QToolButton> that = q;
779 actualMenu->setNoReplayFor(q);
780 if (!mustDeleteActualMenu) { //only if action are not in this widget
781 QObjectPrivate::connect(actualMenu, &QMenu::triggered,
782 this, &QToolButtonPrivate::onMenuTriggered);
783 }
784 QObjectPrivate::connect(actualMenu, &QMenu::aboutToHide,
785 this, &QToolButtonPrivate::updateButtonDown);
786 actualMenu->d_func()->causedPopup.widget = q;
787 actualMenu->d_func()->causedPopup.action = defaultAction;
788 actionsCopy = q->actions(); //(the list of action may be modified in slots)
789
790 // QTBUG-78966, Delay positioning until after aboutToShow().
791 auto positionFunction = [q, horizontal](const QSize &sizeHint) {
792 return positionMenu(q, horizontal, sizeHint); };
793 const auto initialPos = positionFunction(actualMenu->sizeHint());
794 actualMenu->d_func()->exec(initialPos, nullptr, positionFunction);
795
796 if (!that)
797 return;
798
799 QObjectPrivate::disconnect(actualMenu, &QMenu::aboutToHide,
800 this, &QToolButtonPrivate::updateButtonDown);
801 if (menuButtonDown) {
802 // The menu was empty, it didn't actually show up, so it was never hidden either
803 updateButtonDown();
804 }
805
806 if (mustDeleteActualMenu) {
807 delete actualMenu;
808 } else {
809 QObjectPrivate::disconnect(actualMenu, &QMenu::triggered,
810 this, &QToolButtonPrivate::onMenuTriggered);
811 }
812
813 actionsCopy.clear();
814
815 if (repeat)
816 q->setAutoRepeat(true);
817}
818
819void QToolButtonPrivate::updateButtonDown()
820{
821 Q_Q(QToolButton);
822 menuButtonDown = false;
823 if (q->isDown())
824 q->setDown(false);
825 else
826 q->repaint();
827}
828
829void QToolButtonPrivate::onMenuTriggered(QAction *action)
830{
831 Q_Q(QToolButton);
832 if (action && !actionsCopy.contains(action))
833 emit q->triggered(action);
834}
835
836void QToolButtonPrivate::onDefaultActionChanged()
837{
838 Q_Q(QToolButton);
839 if (defaultAction && defaultAction->menu() && !popupModeSetByUser)
840 q->setPopupMode(QToolButton::MenuButtonPopup);
841}
842
843/*! \enum QToolButton::ToolButtonPopupMode
844
845 Describes how a menu should be popped up for tool buttons that has
846 a menu set or contains a list of actions.
847
848 \value DelayedPopup After pressing and holding the tool button
849 down for a certain amount of time (the timeout is style dependent,
850 see QStyle::SH_ToolButton_PopupDelay), the menu is displayed. A
851 typical application example is the "back" button in some web
852 browsers's tool bars. If the user clicks it, the browser simply
853 browses back to the previous page. If the user presses and holds
854 the button down for a while, the tool button shows a menu
855 containing the current history list
856
857 \value MenuButtonPopup In this mode the tool button displays a
858 special arrow to indicate that a menu is present. The menu is
859 displayed when the arrow part of the button is pressed.
860
861 \value InstantPopup The menu is displayed, without delay, when
862 the tool button is pressed. In this mode, the button's own action
863 is not triggered.
864*/
865
866/*!
867 \property QToolButton::popupMode
868 \brief describes the way that popup menus are used with tool buttons
869
870 By default, this property is set to \l DelayedPopup.
871*/
872
873void QToolButton::setPopupMode(ToolButtonPopupMode mode)
874{
875 Q_D(QToolButton);
876 d->popupModeSetByUser = true;
877 d->popupMode = mode;
878}
879
880QToolButton::ToolButtonPopupMode QToolButton::popupMode() const
881{
882 Q_D(const QToolButton);
883 return d->popupMode;
884}
885#endif
886
887/*!
888 \property QToolButton::autoRaise
889 \brief whether auto-raising is enabled or not.
890
891 The default is disabled (i.e. false).
892
893 This property is currently ignored on \macos when using QMacStyle.
894*/
895void QToolButton::setAutoRaise(bool enable)
896{
897 Q_D(QToolButton);
898 d->autoRaise = enable;
899
900 update();
901}
902
903bool QToolButton::autoRaise() const
904{
905 Q_D(const QToolButton);
906 return d->autoRaise;
907}
908
909/*!
910 Sets the default action to \a action.
911
912 If a tool button has a default action, the action defines the
913 following properties of the button:
914
915 \list
916 \li \l {QAbstractButton::}{checkable}
917 \li \l {QAbstractButton::}{checked}
918 \li \l {QWidget::}{enabled}
919 \li \l {QWidget::}{font}
920 \li \l {QAbstractButton::}{icon}
921 \li \l {QToolButton::}{popupMode} (assuming the action has a menu)
922 \li \l {QWidget::}{statusTip}
923 \li \l {QAbstractButton::}{text}
924 \li \l {QWidget::}{toolTip}
925 \li \l {QWidget::}{whatsThis}
926 \endlist
927
928 Other properties, such as \l autoRepeat, are not affected
929 by actions.
930 */
931void QToolButton::setDefaultAction(QAction *action)
932{
933 Q_D(QToolButton);
934#if QT_CONFIG(menu)
935 if (d->defaultAction && d->defaultAction != action) {
936 QObjectPrivate::disconnect(d->defaultAction, &QAction::changed, d,
937 &QToolButtonPrivate::onDefaultActionChanged);
938 }
939 const bool hadMenu = d->hasMenu();
940#endif
941 d->defaultAction = action;
942 if (!action)
943 return;
944 if (!actions().contains(action))
945 addAction(action);
946 QString buttonText = action->iconText();
947 // If iconText() is generated from text(), we need to escape any '&'s so they
948 // don't turn into shortcuts
949 if (QActionPrivate::get(action)->iconText.isEmpty())
950 buttonText.replace("&"_L1, "&&"_L1);
951 setText(buttonText);
952 setIcon(action->icon());
953#if QT_CONFIG(tooltip)
954 setToolTip(action->toolTip());
955#endif
956#if QT_CONFIG(statustip)
957 setStatusTip(action->statusTip());
958#endif
959#if QT_CONFIG(whatsthis)
960 setWhatsThis(action->whatsThis());
961#endif
962#if QT_CONFIG(menu)
963 if (!hadMenu && !d->popupModeSetByUser) {
964 // ### Qt7 Fixme
965 // new 'default' popup mode defined introduced by tool bar. We
966 // should have changed QToolButton's default instead. Do that
967 // in 4.2.
968 if (action->menu()) {
969 setPopupMode(QToolButton::MenuButtonPopup);
970 } else {
971 QObjectPrivate::connect(d->defaultAction, &QAction::changed, d,
972 &QToolButtonPrivate::onDefaultActionChanged);
973 }
974 }
975#endif
976 setCheckable(action->isCheckable());
977 setChecked(action->isChecked());
978 setEnabled(action->isEnabled());
979 if (action->d_func()->fontSet)
980 setFont(action->font());
981}
982
983
984/*!
985 Returns the default action.
986
987 \sa setDefaultAction()
988 */
989QAction *QToolButton::defaultAction() const
990{
991 Q_D(const QToolButton);
992 return d->defaultAction;
993}
994
995/*!
996 \reimp
997 */
998void QToolButton::checkStateSet()
999{
1000 Q_D(QToolButton);
1001 if (d->defaultAction && d->defaultAction->isCheckable())
1002 d->defaultAction->setChecked(isChecked());
1003}
1004
1005/*!
1006 \reimp
1007 */
1008void QToolButton::nextCheckState()
1009{
1010 Q_D(QToolButton);
1011 if (!d->defaultAction)
1012 QAbstractButton::nextCheckState();
1013 else
1014 d->defaultAction->trigger();
1015}
1016
1017/*! \reimp */
1018bool QToolButton::event(QEvent *event)
1019{
1020 switch(event->type()) {
1021 case QEvent::HoverEnter:
1022 case QEvent::HoverLeave:
1023 case QEvent::HoverMove:
1024 if (const QHoverEvent *he = static_cast<const QHoverEvent *>(event))
1025 d_func()->updateHoverControl(he->position().toPoint());
1026 break;
1027 default:
1028 break;
1029 }
1030 return QAbstractButton::event(event);
1031}
1032
1033QT_END_NAMESPACE
1034
1035#include "moc_qtoolbutton.cpp"
\inmodule QtCore\reentrant
Definition qpoint.h:30
QAction * defaultAction
bool updateHoverControl(const QPoint &pos)
Qt::ArrowType arrowType
QStyle::SubControl hoverControl
QPointer< QAction > menuAction
Qt::ToolButtonStyle toolButtonStyle
QStyle::State styleButtonState(QStyle::State state) const override
QStyle::SubControl newHoverControl(const QPoint &pos)
QBasicTimer popupTimer
Combined button and popup list for selecting options.