7#include <qapplication.h>
14#include <qstyleoption.h>
18#if QT_CONFIG(mainwindow)
19#include <qmainwindow.h>
25#include <qstylepainter.h>
26#include <private/qabstractbutton_p.h>
27#include <private/qaction_p.h>
29#include <qeventloop.h>
31#include <private/qmenu_p.h>
36using namespace Qt::StringLiterals;
40 Q_DECLARE_PUBLIC(QToolButton)
78bool QToolButtonPrivate::hasMenu()
const
80 return ((defaultAction && defaultAction->menu())
81 || (menuAction && menuAction->menu())
82 || actions.size() > (defaultAction ? 1 : 0));
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
146
147
148
149
150
151
152
153
154
157
158
159
160QToolButton::QToolButton(QWidget * parent)
161 : QAbstractButton(*
new QToolButtonPrivate, parent)
175#if QT_CONFIG(toolbar)
176 if (qobject_cast<QToolBar*>(parent))
181 arrowType = Qt::NoArrow;
182 menuButtonDown =
false;
183 popupMode = QToolButton::DelayedPopup;
184 popupModeSetByUser =
false;
185 buttonPressed = QToolButtonPrivate::NoButtonPressed;
187 toolButtonStyle = Qt::ToolButtonIconOnly;
188 hoverControl = QStyle::SC_None;
190 q->setFocusPolicy(Qt::TabFocus);
191 q->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed,
192 QSizePolicy::ToolButton));
195 QObjectPrivate::connect(q, &QAbstractButton::pressed,
196 this, &QToolButtonPrivate::onButtonPressed);
197 QObjectPrivate::connect(q, &QAbstractButton::released,
198 this, &QToolButtonPrivate::onButtonReleased);
201 setLayoutItemMargins(QStyle::SE_ToolButtonLayoutItem);
202 delay = q->style()->styleHint(QStyle::SH_ToolButton_PopupDelay,
nullptr, q);
206
207
208
209
210
211
212void QToolButton::initStyleOption(QStyleOptionToolButton *option)
const
217 Q_D(
const QToolButton);
218 option->initFrom(
this);
219 option->iconSize = iconSize();
221#if QT_CONFIG(toolbar)
222 if (parentWidget()) {
223 if (QToolBar *toolBar = qobject_cast<QToolBar *>(parentWidget())) {
224 option->iconSize = toolBar->iconSize();
229 option->text = d->text;
230 option->icon = d->icon;
231 option->arrowType = d->arrowType;
232 option->state = d->styleButtonState(option->state);
233 option->subControls = QStyle::SC_ToolButton;
234 option->activeSubControls = QStyle::SC_None;
236 option->features = QStyleOptionToolButton::None;
237 if (d->popupMode == QToolButton::MenuButtonPopup) {
238 option->subControls |= QStyle::SC_ToolButtonMenu;
239 option->features |= QStyleOptionToolButton::MenuButtonPopup;
241 if (option->state & QStyle::State_MouseOver) {
242 option->activeSubControls = d->hoverControl;
244 if (d->menuButtonDown) {
245 option->state |= QStyle::State_Sunken;
246 option->activeSubControls |= QStyle::SC_ToolButtonMenu;
249 option->state |= QStyle::State_Sunken;
250 option->activeSubControls |= QStyle::SC_ToolButton;
254 if (d->arrowType != Qt::NoArrow)
255 option->features |= QStyleOptionToolButton::Arrow;
256 if (d->popupMode == QToolButton::DelayedPopup)
257 option->features |= QStyleOptionToolButton::PopupDelay;
260 option->features |= QStyleOptionToolButton::HasMenu;
262 if (d->toolButtonStyle == Qt::ToolButtonFollowStyle) {
263 option->toolButtonStyle = Qt::ToolButtonStyle(style()->styleHint(QStyle::SH_ToolButtonStyle, option,
this));
265 option->toolButtonStyle = d->toolButtonStyle;
267 if (option->toolButtonStyle == Qt::ToolButtonTextBesideIcon) {
269 if (d->defaultAction && d->defaultAction->priority() < QAction::NormalPriority)
270 option->toolButtonStyle = Qt::ToolButtonIconOnly;
273 if (d->icon.isNull() && d->arrowType == Qt::NoArrow) {
274 if (!d->text.isEmpty())
275 option->toolButtonStyle = Qt::ToolButtonTextOnly;
276 else if (option->toolButtonStyle != Qt::ToolButtonTextOnly)
277 option->toolButtonStyle = Qt::ToolButtonIconOnly;
281 option->font = font();
285
286
288QToolButton::~QToolButton()
293
294
295QSize QToolButton::sizeHint()
const
297 Q_D(
const QToolButton);
298 if (d->sizeHint.isValid())
303 QStyleOptionToolButton opt;
304 initStyleOption(&opt);
306 QFontMetrics fm = fontMetrics();
307 if (opt.toolButtonStyle != Qt::ToolButtonTextOnly) {
308 QSize icon = opt.iconSize;
313 if (opt.toolButtonStyle != Qt::ToolButtonIconOnly) {
314 QSize textSize = fm.size(Qt::TextShowMnemonic, text());
315 textSize.setWidth(textSize.width() + fm.horizontalAdvance(u' ') * 2);
316 if (opt.toolButtonStyle == Qt::ToolButtonTextUnderIcon) {
317 h += 4 + textSize.height();
318 if (textSize.width() > w)
319 w = textSize.width();
320 }
else if (opt.toolButtonStyle == Qt::ToolButtonTextBesideIcon) {
321 w += 4 + textSize.width();
322 if (textSize.height() > h)
323 h = textSize.height();
325 w = textSize.width();
326 h = textSize.height();
330 opt.rect.setSize(QSize(w, h));
331 if (d->popupMode == MenuButtonPopup)
332 w += style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt,
this);
334 d->sizeHint = style()->sizeFromContents(QStyle::CT_ToolButton, &opt, QSize(w, h),
this);
339
340
341QSize QToolButton::minimumSizeHint()
const
347
348
349
350
351
352
353
354
355
356
357
358
359
362
363
364
365
366
367
368
370Qt::ToolButtonStyle QToolButton::toolButtonStyle()
const
372 Q_D(
const QToolButton);
373 return d->toolButtonStyle;
376Qt::ArrowType QToolButton::arrowType()
const
378 Q_D(
const QToolButton);
383void QToolButton::setToolButtonStyle(Qt::ToolButtonStyle style)
386 if (d->toolButtonStyle == style)
389 d->toolButtonStyle = style;
390 d->sizeHint = QSize();
397void QToolButton::setArrowType(Qt::ArrowType type)
400 if (d->arrowType == type)
404 d->sizeHint = QSize();
412
413
414
415
416void QToolButton::paintEvent(QPaintEvent *)
418 QStylePainter p(
this);
419 QStyleOptionToolButton opt;
420 initStyleOption(&opt);
421 p.drawComplexControl(QStyle::CC_ToolButton, opt);
425
426
427void QToolButton::actionEvent(QActionEvent *event)
430 auto action =
static_cast<QAction *>(event->action());
431 switch (event->type()) {
432 case QEvent::ActionChanged:
433 if (action == d->defaultAction)
434 setDefaultAction(action);
436 case QEvent::ActionAdded:
437 QObjectPrivate::connect(action, &QAction::triggered, d,
438 &QToolButtonPrivate::onActionTriggered);
440 case QEvent::ActionRemoved:
441 if (d->defaultAction == action)
442 d->defaultAction =
nullptr;
444 if (action == d->menuAction)
445 d->menuAction =
nullptr;
447 action->disconnect(
this);
452 QAbstractButton::actionEvent(event);
458 QStyleOptionToolButton opt;
459 q->initStyleOption(&opt);
460 opt.subControls = QStyle::SC_All;
461 hoverControl = q->style()->hitTestComplexControl(QStyle::CC_ToolButton, &opt, pos, q);
462 if (hoverControl == QStyle::SC_None)
465 hoverRect = q->style()->subControlRect(QStyle::CC_ToolButton, &opt, hoverControl, q);
472 QRect lastHoverRect = hoverRect;
473 QStyle::SubControl lastHoverControl = hoverControl;
474 bool doesHover = q->testAttribute(Qt::WA_Hover);
475 if (lastHoverControl != newHoverControl(pos) && doesHover) {
476 q->update(lastHoverRect);
477 q->update(hoverRect);
486 if (QAction *action = qobject_cast<QAction *>(q->sender()))
487 emit q->triggered(action);
492 state = QAbstractButtonPrivate::styleButtonState(state);
494 state |= QStyle::State_On;
496 state |= QStyle::State_AutoRaise;
497 if (!checked && !down)
498 state |= QStyle::State_Raised;
500 state |= QStyle::State_Sunken;
505
506
507void QToolButton::enterEvent(QEnterEvent * e)
512 if (d->defaultAction)
513 d->defaultAction->hover();
514 QAbstractButton::enterEvent(e);
519
520
521void QToolButton::leaveEvent(QEvent * e)
527 QAbstractButton::leaveEvent(e);
532
533
534void QToolButton::timerEvent(QTimerEvent *e)
538 if (e->timerId() == d->popupTimer.timerId()) {
539 d->popupTimer.stop();
540 if (d->menuButtonDown || d->down)
545 QAbstractButton::timerEvent(e);
550
551
552void QToolButton::changeEvent(QEvent *e)
554#if QT_CONFIG(toolbar)
556 if (e->type() == QEvent::ParentChange) {
557 if (qobject_cast<QToolBar*>(parentWidget()))
559 }
else if (e->type() == QEvent::StyleChange
561 || e->type() == QEvent::MacSizeChange
564 d->delay = style()->styleHint(QStyle::SH_ToolButton_PopupDelay,
nullptr,
this);
565 d->setLayoutItemMargins(QStyle::SE_ToolButtonLayoutItem);
568 QAbstractButton::changeEvent(e);
572
573
574void QToolButton::mousePressEvent(QMouseEvent *e)
578 QStyleOptionToolButton opt;
579 initStyleOption(&opt);
580 if (e->button() == Qt::LeftButton && (d->popupMode == MenuButtonPopup)) {
581 QRect popupr = style()->subControlRect(QStyle::CC_ToolButton, &opt,
582 QStyle::SC_ToolButtonMenu,
this);
583 if (popupr.isValid() && popupr.contains(e->position().toPoint())) {
584 d->buttonPressed = QToolButtonPrivate::MenuButtonPressed;
590 d->buttonPressed = QToolButtonPrivate::ToolButtonPressed;
591 QAbstractButton::mousePressEvent(e);
595
596
597void QToolButton::mouseReleaseEvent(QMouseEvent *e)
600 QPointer<QAbstractButton> guard(
this);
601 QAbstractButton::mouseReleaseEvent(e);
603 d->buttonPressed = QToolButtonPrivate::NoButtonPressed;
607
608
609bool QToolButton::hitButton(
const QPoint &pos)
const
611 Q_D(
const QToolButton);
612 if (QAbstractButton::hitButton(pos))
613 return (d->buttonPressed != QToolButtonPrivate::MenuButtonPressed);
620
621
622
623
624
625
626
627
628void QToolButton::setMenu(QMenu* menu)
632 if (d->menuAction == (menu ? menu->menuAction() :
nullptr))
636 removeAction(d->menuAction);
639 d->menuAction = menu->menuAction();
640 addAction(d->menuAction);
642 d->menuAction =
nullptr;
646 d->sizeHint = QSize();
652
653
654
655
656
657QMenu* QToolButton::menu()
const
659 Q_D(
const QToolButton);
661 return d->menuAction->menu();
666
667
668
669
670void QToolButton::showMenu()
673 QPointer<QMenu> menu = d->showMenu();
679 QObject::connect(menu, &QMenu::aboutToHide, &loop, &QEventLoop::quit);
680 QObject::connect(menu, &QObject::destroyed, &loop, &QEventLoop::quit);
684void QToolButtonPrivate::onButtonPressed()
689 if (popupMode == QToolButton::MenuButtonPopup)
691 else if (delay > 0 && popupMode == QToolButton::DelayedPopup)
692 popupTimer.start(delay, q);
693 else if (delay == 0 || popupMode == QToolButton::InstantPopup)
697void QToolButtonPrivate::onButtonReleased()
702static QPoint positionMenu(
const QToolButton *q,
bool horizontal,
706 const QRect rect = q->rect();
707 const QRect screen = QWidgetPrivate::availableScreenGeometry(q, q->mapToGlobal(rect.center()));
709 if (q->isRightToLeft()) {
710 if (q->mapToGlobal(QPoint(0, rect.bottom())).y() + sh.height() <= screen.bottom()) {
711 p = q->mapToGlobal(rect.bottomRight());
713 p = q->mapToGlobal(rect.topRight() - QPoint(0, sh.height()));
715 p.rx() -= sh.width();
717 if (q->mapToGlobal(QPoint(0, rect.bottom())).y() + sh.height() <= screen.bottom()) {
718 p = q->mapToGlobal(rect.bottomLeft());
720 p = q->mapToGlobal(rect.topLeft() - QPoint(0, sh.height()));
724 if (q->isRightToLeft()) {
725 if (q->mapToGlobal(QPoint(rect.left(), 0)).x() - sh.width() <= screen.x()) {
726 p = q->mapToGlobal(rect.topRight());
728 p = q->mapToGlobal(rect.topLeft());
729 p.rx() -= sh.width();
732 if (q->mapToGlobal(QPoint(rect.right(), 0)).x() + sh.width() <= screen.right()) {
733 p = q->mapToGlobal(rect.topRight());
735 p = q->mapToGlobal(rect.topLeft() - QPoint(sh.width(), 0));
744 p.rx() = qMax(screen.left(), qMin(p.x(), screen.right() - sh.width()));
745 p.ry() = qMax(screen.top(), qMin(p.y() + 1, screen.bottom()));
751QMenu *QToolButtonPrivate::showMenu()
756 menuButtonDown =
false;
763 menuButtonDown =
true;
767 QPointer<QMenu> actualMenu;
768 bool mustDeleteActualMenu =
false;
770 actualMenu = menuAction->menu();
771 }
else if (defaultAction && defaultAction->menu()) {
772 actualMenu = defaultAction->menu();
774 actualMenu =
new QMenu(q);
775 mustDeleteActualMenu =
true;
776 for (
int i = 0; i < actions.size(); i++)
777 actualMenu->addAction(actions.at(i));
779 repeat = q->autoRepeat();
780 q->setAutoRepeat(
false);
781 bool horizontal =
true;
782#if QT_CONFIG(toolbar)
783 QToolBar *tb = qobject_cast<QToolBar*>(parent);
784 if (tb && tb->orientation() == Qt::Vertical)
787 QPointer<QToolButton> that = q;
788 actualMenu->setNoReplayFor(q);
789 if (!mustDeleteActualMenu) {
790 QObjectPrivate::connect(actualMenu, &QMenu::triggered,
791 this, &QToolButtonPrivate::onMenuTriggered);
793 QObjectPrivate::connect(actualMenu, &QMenu::aboutToHide,
794 this, &QToolButtonPrivate::updateButtonDown);
795 actualMenu->d_func()->causedPopup.widget = q;
796 actualMenu->d_func()->causedPopup.action = defaultAction;
797 actionsCopy = q->actions();
800 auto positionFunction = [q, horizontal](
const QSize &sizeHint) {
801 return positionMenu(q, horizontal, sizeHint); };
802 const auto initialPos = positionFunction(actualMenu->sizeHint());
804 auto cleanup = [that, mustDeleteActualMenu, actualMenu] {
807 QToolButtonPrivate *d = that->d_func();
809 if (d->menuButtonDown) {
811 d->updateButtonDown();
814 if (mustDeleteActualMenu) {
815 actualMenu->deleteLater();
816 }
else if (actualMenu) {
817 QObjectPrivate::disconnect(actualMenu, &QMenu::aboutToHide,
818 d, &QToolButtonPrivate::updateButtonDown);
819 QObjectPrivate::disconnect(actualMenu, &QMenu::triggered,
820 d, &QToolButtonPrivate::onMenuTriggered);
823 d->actionsCopy.clear();
825 that->setAutoRepeat(
true);
833 auto runCleanupWhenDone = [actualMenu, cleanup] {
834 if (actualMenu && actualMenu->d_func()->actionAboutToTrigger) {
835 QObject::connect(actualMenu, &QMenu::triggered, actualMenu, cleanup,
836 Qt::SingleShotConnection);
842 QMetaObject::Connection hideConnection = QObject::connect(
843 actualMenu, &QMenu::aboutToHide, q, runCleanupWhenDone,
844 Qt::SingleShotConnection);
845 actualMenu->d_func()->popup(initialPos,
nullptr, positionFunction);
847 if (!actualMenu || !actualMenu->isVisible()) {
850 QObject::disconnect(hideConnection);
857void QToolButtonPrivate::updateButtonDown()
860 menuButtonDown =
false;
867void QToolButtonPrivate::onMenuTriggered(QAction *action)
870 if (action && !actionsCopy.contains(action))
871 emit q->triggered(action);
874void QToolButtonPrivate::onDefaultActionChanged()
877 if (defaultAction && defaultAction->menu() && !popupModeSetByUser)
878 q->setPopupMode(QToolButton::MenuButtonPopup);
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
905
906
907
908
909
911void QToolButton::setPopupMode(ToolButtonPopupMode mode)
914 d->popupModeSetByUser =
true;
918QToolButton::ToolButtonPopupMode QToolButton::popupMode()
const
920 Q_D(
const QToolButton);
926
927
928
929
930
931
932
933void QToolButton::setAutoRaise(
bool enable)
936 d->autoRaise = enable;
941bool QToolButton::autoRaise()
const
943 Q_D(
const QToolButton);
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969void QToolButton::setDefaultAction(QAction *action)
973 if (d->defaultAction && d->defaultAction != action) {
974 QObjectPrivate::disconnect(d->defaultAction, &QAction::changed, d,
975 &QToolButtonPrivate::onDefaultActionChanged);
977 const bool hadMenu = d->hasMenu();
979 d->defaultAction = action;
982 if (!actions().contains(action))
984 QString buttonText = action->iconText();
987 if (QActionPrivate::get(action)->iconText.isEmpty())
988 buttonText.replace(
"&"_L1,
"&&"_L1);
990 setIcon(action->icon());
991#if QT_CONFIG(tooltip)
992 setToolTip(action->toolTip());
994#if QT_CONFIG(statustip)
995 setStatusTip(action->statusTip());
997#if QT_CONFIG(whatsthis)
998 setWhatsThis(action->whatsThis());
1001 if (!hadMenu && !d->popupModeSetByUser) {
1006 if (action->menu()) {
1007 setPopupMode(QToolButton::MenuButtonPopup);
1009 QObjectPrivate::connect(d->defaultAction, &QAction::changed, d,
1010 &QToolButtonPrivate::onDefaultActionChanged);
1014 setCheckable(action->isCheckable());
1015 setChecked(action->isChecked());
1016 setEnabled(action->isEnabled());
1017 if (action->d_func()->fontSet)
1018 setFont(action->font());
1023
1024
1025
1026
1027QAction *QToolButton::defaultAction()
const
1029 Q_D(
const QToolButton);
1030 return d->defaultAction;
1034
1035
1036void QToolButton::checkStateSet()
1039 if (d->defaultAction && d->defaultAction->isCheckable())
1040 d->defaultAction->setChecked(isChecked());
1044
1045
1046void QToolButton::nextCheckState()
1049 if (!d->defaultAction)
1050 QAbstractButton::nextCheckState();
1052 d->defaultAction->trigger();
1056bool QToolButton::event(QEvent *event)
1058 switch(event->type()) {
1059 case QEvent::HoverEnter:
1060 case QEvent::HoverLeave:
1061 case QEvent::HoverMove:
1062 if (
const QHoverEvent *he =
static_cast<
const QHoverEvent *>(event))
1063 d_func()->updateHoverControl(he->position().toPoint());
1068 return QAbstractButton::event(event);
1073#include "moc_qtoolbutton.cpp"
\inmodule QtCore\reentrant
Combined button and popup list for selecting options.