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