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>
30#include <private/qmenu_p.h>
35using namespace Qt::StringLiterals;
39 Q_DECLARE_PUBLIC(QToolButton)
77bool QToolButtonPrivate::hasMenu()
const
79 return ((defaultAction && defaultAction->menu())
80 || (menuAction && menuAction->menu())
81 || actions.size() > (defaultAction ? 1 : 0));
86
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
145
146
147
148
149
150
151
152
153
156
157
158
159QToolButton::QToolButton(QWidget * parent)
160 : QAbstractButton(*
new QToolButtonPrivate, parent)
174#if QT_CONFIG(toolbar)
175 if (qobject_cast<QToolBar*>(parent))
180 arrowType = Qt::NoArrow;
181 menuButtonDown =
false;
182 popupMode = QToolButton::DelayedPopup;
183 popupModeSetByUser =
false;
184 buttonPressed = QToolButtonPrivate::NoButtonPressed;
186 toolButtonStyle = Qt::ToolButtonIconOnly;
187 hoverControl = QStyle::SC_None;
189 q->setFocusPolicy(Qt::TabFocus);
190 q->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed,
191 QSizePolicy::ToolButton));
194 QObjectPrivate::connect(q, &QAbstractButton::pressed,
195 this, &QToolButtonPrivate::onButtonPressed);
196 QObjectPrivate::connect(q, &QAbstractButton::released,
197 this, &QToolButtonPrivate::onButtonReleased);
200 setLayoutItemMargins(QStyle::SE_ToolButtonLayoutItem);
201 delay = q->style()->styleHint(QStyle::SH_ToolButton_PopupDelay,
nullptr, q);
205
206
207
208
209
210
211void QToolButton::initStyleOption(QStyleOptionToolButton *option)
const
216 Q_D(
const QToolButton);
217 option->initFrom(
this);
218 option->iconSize = iconSize();
220#if QT_CONFIG(toolbar)
221 if (parentWidget()) {
222 if (QToolBar *toolBar = qobject_cast<QToolBar *>(parentWidget())) {
223 option->iconSize = toolBar->iconSize();
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;
235 option->features = QStyleOptionToolButton::None;
236 if (d->popupMode == QToolButton::MenuButtonPopup) {
237 option->subControls |= QStyle::SC_ToolButtonMenu;
238 option->features |= QStyleOptionToolButton::MenuButtonPopup;
240 if (option->state & QStyle::State_MouseOver) {
241 option->activeSubControls = d->hoverControl;
243 if (d->menuButtonDown) {
244 option->state |= QStyle::State_Sunken;
245 option->activeSubControls |= QStyle::SC_ToolButtonMenu;
248 option->state |= QStyle::State_Sunken;
249 option->activeSubControls |= QStyle::SC_ToolButton;
253 if (d->arrowType != Qt::NoArrow)
254 option->features |= QStyleOptionToolButton::Arrow;
255 if (d->popupMode == QToolButton::DelayedPopup)
256 option->features |= QStyleOptionToolButton::PopupDelay;
259 option->features |= QStyleOptionToolButton::HasMenu;
261 if (d->toolButtonStyle == Qt::ToolButtonFollowStyle) {
262 option->toolButtonStyle = Qt::ToolButtonStyle(style()->styleHint(QStyle::SH_ToolButtonStyle, option,
this));
264 option->toolButtonStyle = d->toolButtonStyle;
266 if (option->toolButtonStyle == Qt::ToolButtonTextBesideIcon) {
268 if (d->defaultAction && d->defaultAction->priority() < QAction::NormalPriority)
269 option->toolButtonStyle = Qt::ToolButtonIconOnly;
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;
280 option->font = font();
284
285
287QToolButton::~QToolButton()
292
293
294QSize QToolButton::sizeHint()
const
296 Q_D(
const QToolButton);
297 if (d->sizeHint.isValid())
302 QStyleOptionToolButton opt;
303 initStyleOption(&opt);
305 QFontMetrics fm = fontMetrics();
306 if (opt.toolButtonStyle != Qt::ToolButtonTextOnly) {
307 QSize icon = opt.iconSize;
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();
324 w = textSize.width();
325 h = textSize.height();
329 opt.rect.setSize(QSize(w, h));
330 if (d->popupMode == MenuButtonPopup)
331 w += style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt,
this);
333 d->sizeHint = style()->sizeFromContents(QStyle::CT_ToolButton, &opt, QSize(w, h),
this);
338
339
340QSize QToolButton::minimumSizeHint()
const
346
347
348
349
350
351
352
353
354
355
356
357
358
361
362
363
364
365
366
367
369Qt::ToolButtonStyle QToolButton::toolButtonStyle()
const
371 Q_D(
const QToolButton);
372 return d->toolButtonStyle;
375Qt::ArrowType QToolButton::arrowType()
const
377 Q_D(
const QToolButton);
382void QToolButton::setToolButtonStyle(Qt::ToolButtonStyle style)
385 if (d->toolButtonStyle == style)
388 d->toolButtonStyle = style;
389 d->sizeHint = QSize();
396void QToolButton::setArrowType(Qt::ArrowType type)
399 if (d->arrowType == type)
403 d->sizeHint = QSize();
411
412
413
414
415void QToolButton::paintEvent(QPaintEvent *)
417 QStylePainter p(
this);
418 QStyleOptionToolButton opt;
419 initStyleOption(&opt);
420 p.drawComplexControl(QStyle::CC_ToolButton, opt);
424
425
426void QToolButton::actionEvent(QActionEvent *event)
429 auto action =
static_cast<QAction *>(event->action());
430 switch (event->type()) {
431 case QEvent::ActionChanged:
432 if (action == d->defaultAction)
433 setDefaultAction(action);
435 case QEvent::ActionAdded:
436 QObjectPrivate::connect(action, &QAction::triggered, d,
437 &QToolButtonPrivate::onActionTriggered);
439 case QEvent::ActionRemoved:
440 if (d->defaultAction == action)
441 d->defaultAction =
nullptr;
443 if (action == d->menuAction)
444 d->menuAction =
nullptr;
446 action->disconnect(
this);
451 QAbstractButton::actionEvent(event);
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)
464 hoverRect = q->style()->subControlRect(QStyle::CC_ToolButton, &opt, hoverControl, q);
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);
485 if (QAction *action = qobject_cast<QAction *>(q->sender()))
486 emit q->triggered(action);
491 state = QAbstractButtonPrivate::styleButtonState(state);
493 state |= QStyle::State_On;
495 state |= QStyle::State_AutoRaise;
496 if (!checked && !down)
497 state |= QStyle::State_Raised;
499 state |= QStyle::State_Sunken;
504
505
506void QToolButton::enterEvent(QEnterEvent * e)
511 if (d->defaultAction)
512 d->defaultAction->hover();
513 QAbstractButton::enterEvent(e);
518
519
520void QToolButton::leaveEvent(QEvent * e)
526 QAbstractButton::leaveEvent(e);
531
532
533void QToolButton::timerEvent(QTimerEvent *e)
537 if (e->timerId() == d->popupTimer.timerId()) {
542 QAbstractButton::timerEvent(e);
547
548
549void QToolButton::changeEvent(QEvent *e)
551#if QT_CONFIG(toolbar)
553 if (e->type() == QEvent::ParentChange) {
554 if (qobject_cast<QToolBar*>(parentWidget()))
556 }
else if (e->type() == QEvent::StyleChange
558 || e->type() == QEvent::MacSizeChange
561 d->delay = style()->styleHint(QStyle::SH_ToolButton_PopupDelay,
nullptr,
this);
562 d->setLayoutItemMargins(QStyle::SE_ToolButtonLayoutItem);
565 QAbstractButton::changeEvent(e);
569
570
571void QToolButton::mousePressEvent(QMouseEvent *e)
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;
587 d->buttonPressed = QToolButtonPrivate::ToolButtonPressed;
588 QAbstractButton::mousePressEvent(e);
592
593
594void QToolButton::mouseReleaseEvent(QMouseEvent *e)
597 QPointer<QAbstractButton> guard(
this);
598 QAbstractButton::mouseReleaseEvent(e);
600 d->buttonPressed = QToolButtonPrivate::NoButtonPressed;
604
605
606bool QToolButton::hitButton(
const QPoint &pos)
const
608 Q_D(
const QToolButton);
609 if (QAbstractButton::hitButton(pos))
610 return (d->buttonPressed != QToolButtonPrivate::MenuButtonPressed);
617
618
619
620
621
622
623
624
625void QToolButton::setMenu(QMenu* menu)
629 if (d->menuAction == (menu ? menu->menuAction() :
nullptr))
633 removeAction(d->menuAction);
636 d->menuAction = menu->menuAction();
637 addAction(d->menuAction);
639 d->menuAction =
nullptr;
643 d->sizeHint = QSize();
649
650
651
652
653
654QMenu* QToolButton::menu()
const
656 Q_D(
const QToolButton);
658 return d->menuAction->menu();
663
664
665
666
667void QToolButton::showMenu()
671 d->menuButtonDown =
false;
675 if (d->menuButtonDown)
679 d->menuButtonDown =
true;
681 d->popupTimer.stop();
685void QToolButtonPrivate::onButtonPressed()
690 if (popupMode == QToolButton::MenuButtonPopup)
692 else if (delay > 0 && popupMode == QToolButton::DelayedPopup)
693 popupTimer.start(delay, q);
694 else if (delay == 0 || popupMode == QToolButton::InstantPopup)
698void QToolButtonPrivate::onButtonReleased()
703static QPoint positionMenu(
const QToolButton *q,
bool horizontal,
707 const QRect rect = q->rect();
708 const QRect screen = QWidgetPrivate::availableScreenGeometry(q, q->mapToGlobal(rect.center()));
710 if (q->isRightToLeft()) {
711 if (q->mapToGlobal(QPoint(0, rect.bottom())).y() + sh.height() <= screen.bottom()) {
712 p = q->mapToGlobal(rect.bottomRight());
714 p = q->mapToGlobal(rect.topRight() - QPoint(0, sh.height()));
716 p.rx() -= sh.width();
718 if (q->mapToGlobal(QPoint(0, rect.bottom())).y() + sh.height() <= screen.bottom()) {
719 p = q->mapToGlobal(rect.bottomLeft());
721 p = q->mapToGlobal(rect.topLeft() - QPoint(0, sh.height()));
725 if (q->isRightToLeft()) {
726 if (q->mapToGlobal(QPoint(rect.left(), 0)).x() - sh.width() <= screen.x()) {
727 p = q->mapToGlobal(rect.topRight());
729 p = q->mapToGlobal(rect.topLeft());
730 p.rx() -= sh.width();
733 if (q->mapToGlobal(QPoint(rect.right(), 0)).x() + sh.width() <= screen.right()) {
734 p = q->mapToGlobal(rect.topRight());
736 p = q->mapToGlobal(rect.topLeft() - QPoint(sh.width(), 0));
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()));
750void QToolButtonPrivate::popupTimerDone()
754 if (!menuButtonDown && !down)
757 menuButtonDown =
true;
758 QPointer<QMenu> actualMenu;
759 bool mustDeleteActualMenu =
false;
761 actualMenu = menuAction->menu();
762 }
else if (defaultAction && defaultAction->menu()) {
763 actualMenu = defaultAction->menu();
765 actualMenu =
new QMenu(q);
766 mustDeleteActualMenu =
true;
767 for (
int i = 0; i < actions.size(); i++)
768 actualMenu->addAction(actions.at(i));
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)
778 QPointer<QToolButton> that = q;
779 actualMenu->setNoReplayFor(q);
780 if (!mustDeleteActualMenu) {
781 QObjectPrivate::connect(actualMenu, &QMenu::triggered,
782 this, &QToolButtonPrivate::onMenuTriggered);
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();
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);
799 QObjectPrivate::disconnect(actualMenu, &QMenu::aboutToHide,
800 this, &QToolButtonPrivate::updateButtonDown);
801 if (menuButtonDown) {
806 if (mustDeleteActualMenu) {
809 QObjectPrivate::disconnect(actualMenu, &QMenu::triggered,
810 this, &QToolButtonPrivate::onMenuTriggered);
816 q->setAutoRepeat(
true);
819void QToolButtonPrivate::updateButtonDown()
822 menuButtonDown =
false;
829void QToolButtonPrivate::onMenuTriggered(QAction *action)
832 if (action && !actionsCopy.contains(action))
833 emit q->triggered(action);
836void QToolButtonPrivate::onDefaultActionChanged()
839 if (defaultAction && defaultAction->menu() && !popupModeSetByUser)
840 q->setPopupMode(QToolButton::MenuButtonPopup);
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
867
868
869
870
871
873void QToolButton::setPopupMode(ToolButtonPopupMode mode)
876 d->popupModeSetByUser =
true;
880QToolButton::ToolButtonPopupMode QToolButton::popupMode()
const
882 Q_D(
const QToolButton);
888
889
890
891
892
893
894
895void QToolButton::setAutoRaise(
bool enable)
898 d->autoRaise = enable;
903bool QToolButton::autoRaise()
const
905 Q_D(
const QToolButton);
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931void QToolButton::setDefaultAction(QAction *action)
935 if (d->defaultAction && d->defaultAction != action) {
936 QObjectPrivate::disconnect(d->defaultAction, &QAction::changed, d,
937 &QToolButtonPrivate::onDefaultActionChanged);
939 const bool hadMenu = d->hasMenu();
941 d->defaultAction = action;
944 if (!actions().contains(action))
946 QString buttonText = action->iconText();
949 if (QActionPrivate::get(action)->iconText.isEmpty())
950 buttonText.replace(
"&"_L1,
"&&"_L1);
952 setIcon(action->icon());
953#if QT_CONFIG(tooltip)
954 setToolTip(action->toolTip());
956#if QT_CONFIG(statustip)
957 setStatusTip(action->statusTip());
959#if QT_CONFIG(whatsthis)
960 setWhatsThis(action->whatsThis());
963 if (!hadMenu && !d->popupModeSetByUser) {
968 if (action->menu()) {
969 setPopupMode(QToolButton::MenuButtonPopup);
971 QObjectPrivate::connect(d->defaultAction, &QAction::changed, d,
972 &QToolButtonPrivate::onDefaultActionChanged);
976 setCheckable(action->isCheckable());
977 setChecked(action->isChecked());
978 setEnabled(action->isEnabled());
979 if (action->d_func()->fontSet)
980 setFont(action->font());
985
986
987
988
989QAction *QToolButton::defaultAction()
const
991 Q_D(
const QToolButton);
992 return d->defaultAction;
996
997
998void QToolButton::checkStateSet()
1001 if (d->defaultAction && d->defaultAction->isCheckable())
1002 d->defaultAction->setChecked(isChecked());
1006
1007
1008void QToolButton::nextCheckState()
1011 if (!d->defaultAction)
1012 QAbstractButton::nextCheckState();
1014 d->defaultAction->trigger();
1018bool QToolButton::event(QEvent *event)
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());
1030 return QAbstractButton::event(event);
1035#include "moc_qtoolbutton.cpp"
\inmodule QtCore\reentrant
Combined button and popup list for selecting options.