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
4#include "qtoolbar.h"
5
6#include <qapplication.h>
7#if QT_CONFIG(draganddrop)
8#include <qdrag.h>
9#endif
10#include <qevent.h>
11#include <qlayout.h>
12#include <qmainwindow.h>
13#include <qmenu.h>
14#include <qmimedata.h>
15#include <qstylepainter.h>
16#include <qstyleoption.h>
17#include <qtoolbutton.h>
18#include <qwidgetaction.h>
19#include <private/qwidgetaction_p.h>
20#include <private/qmainwindowlayout_p.h>
21#include <private/qhighdpiscaling_p.h>
22
23#ifdef Q_OS_MACOS
24#include <qpa/qplatformnativeinterface.h>
25#endif
26
27#include "qtoolbar_p.h"
28#include "qtoolbarseparator_p.h"
29#include "qtoolbarlayout_p.h"
30
31#include "qdebug.h"
32
33#define POPUP_TIMER_INTERVAL 500
34
35QT_BEGIN_NAMESPACE
36
37using namespace Qt::StringLiterals;
38
39// qmainwindow.cpp
40extern QMainWindowLayout *qt_mainwindow_layout(const QMainWindow *window);
41
42/******************************************************************************
43** QToolBarPrivate
44*/
45
46void QToolBarPrivate::init()
47{
48 Q_Q(QToolBar);
49 q->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed));
50 q->setBackgroundRole(QPalette::Button);
51 q->setAttribute(Qt::WA_Hover);
52 q->setAttribute(Qt::WA_X11NetWmWindowTypeToolBar);
53
54 QStyle *style = q->style();
55 int e = style->pixelMetric(metric: QStyle::PM_ToolBarIconSize, option: nullptr, widget: q);
56 iconSize = QSize(e, e);
57
58 layout = new QToolBarLayout(q);
59 layout->updateMarginAndSpacing();
60
61 toggleViewAction = new QAction(q);
62 toggleViewAction->setCheckable(true);
63 q->setMovable(q->style()->styleHint(stylehint: QStyle::SH_ToolBar_Movable, opt: nullptr, widget: q ));
64 QObject::connect(sender: toggleViewAction, SIGNAL(triggered(bool)), receiver: q, SLOT(_q_toggleView(bool)));
65}
66
67void QToolBarPrivate::_q_toggleView(bool b)
68{
69 Q_Q(QToolBar);
70 if (b == q->isHidden()) {
71 if (b)
72 q->show();
73 else
74 q->close();
75 }
76}
77
78void QToolBarPrivate::_q_updateIconSize(const QSize &sz)
79{
80 Q_Q(QToolBar);
81 if (!explicitIconSize) {
82 // iconSize not explicitly set
83 q->setIconSize(sz);
84 explicitIconSize = false;
85 }
86}
87
88void QToolBarPrivate::_q_updateToolButtonStyle(Qt::ToolButtonStyle style)
89{
90 Q_Q(QToolBar);
91 if (!explicitToolButtonStyle) {
92 q->setToolButtonStyle(style);
93 explicitToolButtonStyle = false;
94 }
95}
96
97void QToolBarPrivate::updateWindowFlags(bool floating, bool unplug)
98{
99 Q_Q(QToolBar);
100 Qt::WindowFlags flags = floating ? Qt::Tool : Qt::Widget;
101
102 flags |= Qt::FramelessWindowHint;
103
104#if QT_CONFIG(draganddrop)
105 // If we are performing a platform drag the flag is not needed and we want to avoid recreating
106 // the platform window when it would be removed later
107 if (unplug && !QMainWindowLayout::needsPlatformDrag())
108 flags |= Qt::X11BypassWindowManagerHint;
109#else
110 Q_UNUSED(unplug);
111#endif
112
113 q->setWindowFlags(flags);
114}
115
116void QToolBarPrivate::setWindowState(bool floating, bool unplug, const QRect &rect)
117{
118 Q_Q(QToolBar);
119 bool visible = !q->isHidden();
120 bool wasFloating = q->isFloating(); // ...is also currently using popup menus
121
122 updateWindowFlags(floating, unplug);
123
124 if (floating != wasFloating)
125 layout->checkUsePopupMenu();
126
127 if (!rect.isNull())
128 q->setGeometry(rect);
129
130 if (visible)
131 q->show();
132
133 if (floating != wasFloating)
134 emit q->topLevelChanged(topLevel: floating);
135}
136
137void QToolBarPrivate::initDrag(const QPoint &pos)
138{
139 Q_Q(QToolBar);
140
141 if (state != nullptr)
142 return;
143
144 QMainWindow *win = qobject_cast<QMainWindow*>(object: parent);
145 Q_ASSERT(win != nullptr);
146 QMainWindowLayout *layout = qt_mainwindow_layout(window: win);
147 Q_ASSERT(layout != nullptr);
148 if (layout->pluggingWidget != nullptr) // the main window is animating a docking operation
149 return;
150
151 state = new DragState;
152 state->pressPos = pos;
153 state->dragging = false;
154 state->moving = false;
155 state->widgetItem = nullptr;
156
157 if (q->isRightToLeft())
158 state->pressPos = QPoint(q->width() - state->pressPos.x(), state->pressPos.y());
159}
160
161void QToolBarPrivate::startDrag(bool moving)
162{
163 Q_Q(QToolBar);
164
165 Q_ASSERT(state != nullptr);
166
167 if ((moving && state->moving) || state->dragging)
168 return;
169
170 QMainWindow *win = qobject_cast<QMainWindow*>(object: parent);
171 Q_ASSERT(win != nullptr);
172 QMainWindowLayout *layout = qt_mainwindow_layout(window: win);
173 Q_ASSERT(layout != nullptr);
174
175#if QT_CONFIG(draganddrop)
176 const bool wasFloating = q->isFloating();
177#endif
178
179 if (!moving) {
180 state->widgetItem = layout->unplug(widget: q, scope: QDockWidgetPrivate::DragScope::Group);
181 Q_ASSERT(state->widgetItem != nullptr);
182 }
183 state->dragging = !moving;
184 state->moving = moving;
185
186#if QT_CONFIG(draganddrop)
187 if (QMainWindowLayout::needsPlatformDrag() && state->dragging) {
188 auto result = layout->performPlatformWidgetDrag(widgetItem: state->widgetItem, pressPosition: state->pressPos);
189 if (result == Qt::IgnoreAction && !wasFloating) {
190 layout->revert(widgetItem: state->widgetItem);
191 delete state;
192 state = nullptr;
193 } else {
194 endDrag();
195 }
196 }
197#endif
198}
199
200void QToolBarPrivate::endDrag()
201{
202 Q_Q(QToolBar);
203 Q_ASSERT(state != nullptr);
204
205 q->releaseMouse();
206
207 if (state->dragging) {
208 QMainWindowLayout *layout = qt_mainwindow_layout(window: qobject_cast<QMainWindow *>(object: q->parentWidget()));
209 Q_ASSERT(layout != nullptr);
210
211 if (!layout->plug(widgetItem: state->widgetItem)) {
212 if (q->isFloatable()) {
213 layout->restore();
214 setWindowState(floating: true); // gets rid of the X11BypassWindowManager window flag
215 // and activates the resizer
216 q->activateWindow();
217 } else {
218 layout->revert(widgetItem: state->widgetItem);
219 }
220 }
221 }
222
223 delete state;
224 state = nullptr;
225}
226
227bool QToolBarPrivate::mousePressEvent(QMouseEvent *event)
228{
229 Q_Q(QToolBar);
230 QStyleOptionToolBar opt;
231 q->initStyleOption(option: &opt);
232 if (q->style()->subElementRect(subElement: QStyle::SE_ToolBarHandle, option: &opt, widget: q).contains(p: event->position().toPoint()) == false) {
233#ifdef Q_OS_MACOS
234 // When using the unified toolbar on OS X, the user can click and
235 // drag between toolbar contents to move the window. Make this work by
236 // implementing the standard mouse-dragging code and then call
237 // window->move() in mouseMoveEvent below.
238 if (QMainWindow *mainWindow = qobject_cast<QMainWindow *>(parent)) {
239 if (mainWindow->toolBarArea(q) == Qt::TopToolBarArea
240 && mainWindow->unifiedTitleAndToolBarOnMac()
241 && q->childAt(event->pos()) == 0) {
242 macWindowDragging = true;
243 macWindowDragPressPosition = event->pos();
244 return true;
245 }
246 }
247#endif
248 return false;
249 }
250
251 if (event->button() != Qt::LeftButton)
252 return true;
253
254 if (!layout->movable())
255 return true;
256
257 initDrag(pos: event->position().toPoint());
258 return true;
259}
260
261bool QToolBarPrivate::mouseReleaseEvent(QMouseEvent*)
262{
263#if QT_CONFIG(draganddrop)
264 // if we are peforming a platform drag ignore the release here and end the drag when the actual
265 // drag ends.
266 if (QMainWindowLayout::needsPlatformDrag())
267 return false;
268#endif
269
270 if (state != nullptr) {
271 endDrag();
272 return true;
273 } else {
274#ifdef Q_OS_MACOS
275 if (!macWindowDragging)
276 return false;
277 macWindowDragging = false;
278 macWindowDragPressPosition = QPoint();
279 return true;
280#endif
281 return false;
282 }
283}
284
285bool QToolBarPrivate::mouseMoveEvent(QMouseEvent *event)
286{
287 Q_Q(QToolBar);
288
289 if (!state) {
290#ifdef Q_OS_MACOS
291 if (!macWindowDragging)
292 return false;
293 QWidget *w = q->window();
294 const QPoint delta = event->pos() - macWindowDragPressPosition;
295 w->move(w->pos() + delta);
296 return true;
297#endif
298 return false;
299 }
300
301 QMainWindow *win = qobject_cast<QMainWindow*>(object: parent);
302 if (win == nullptr)
303 return true;
304
305 QMainWindowLayout *layout = qt_mainwindow_layout(window: win);
306 Q_ASSERT(layout != nullptr);
307
308 if (layout->pluggingWidget == nullptr
309 && (event->position().toPoint() - state->pressPos).manhattanLength() > QApplication::startDragDistance()) {
310 const bool wasDragging = state->dragging;
311 const bool moving = !q->isWindow() && (orientation == Qt::Vertical ?
312 event->position().toPoint().x() >= 0 && event->position().toPoint().x() < q->width() :
313 event->position().toPoint().y() >= 0 && event->position().toPoint().y() < q->height());
314
315 startDrag(moving);
316 if (!moving && !wasDragging)
317 q->grabMouse();
318 }
319
320 if (!state) {
321 q->releaseMouse();
322 return true;
323 }
324
325 if (state->dragging) {
326 QPoint pos = event->globalPosition().toPoint();
327 // if we are right-to-left, we move so as to keep the right edge the same distance
328 // from the mouse
329 if (q->isLeftToRight())
330 pos -= state->pressPos;
331 else
332 pos += QPoint(state->pressPos.x() - q->width(), -state->pressPos.y());
333
334 q->move(pos);
335 layout->hover(hoverTarget: state->widgetItem, mousePos: event->globalPosition().toPoint());
336 } else if (state->moving) {
337
338 const QPoint rtl(q->width() - state->pressPos.x(), state->pressPos.y()); //for RTL
339 const QPoint globalPressPos = q->mapToGlobal(q->isRightToLeft() ? rtl : state->pressPos);
340 int pos = 0;
341
342 const QWindow *handle = q->window() ? q->window()->windowHandle() : nullptr;
343 const QPoint delta = handle
344 ? QHighDpi::fromNativePixels(value: event->globalPosition(), context: handle).toPoint()
345 - QHighDpi::fromNativePixels(value: globalPressPos, context: handle)
346 : event->globalPosition().toPoint() - globalPressPos;
347
348 if (orientation == Qt::Vertical) {
349 pos = q->y() + delta.y();
350 } else {
351 if (q->isRightToLeft()) {
352 pos = win->width() - q->width() - q->x() - delta.x();
353 } else {
354 pos = q->x() + delta.x();
355 }
356 }
357
358 layout->moveToolBar(toolbar: q, pos);
359 }
360 return true;
361}
362
363void QToolBarPrivate::unplug(const QRect &_r)
364{
365 Q_Q(QToolBar);
366 QRect r = _r;
367 r.moveTopLeft(p: q->mapToGlobal(QPoint(0, 0)));
368 setWindowState(floating: true, unplug: true, rect: r);
369 layout->setExpanded(false);
370}
371
372void QToolBarPrivate::plug(const QRect &r)
373{
374 setWindowState(floating: false, unplug: false, rect: r);
375}
376
377/******************************************************************************
378** QToolBar
379*/
380
381/*!
382 \class QToolBar
383
384 \brief The QToolBar class provides a movable panel that contains a
385 set of controls.
386
387 \ingroup mainwindow-classes
388 \inmodule QtWidgets
389
390 A toolbar is typically created by calling
391 \l QMainWindow::addToolBar(const QString &title), but it can also
392 be added as the first widget in a QVBoxLayout, for example.
393
394 Toolbar buttons are added by adding \e actions, using addAction()
395 or insertAction(). Groups of buttons can be separated using
396 addSeparator() or insertSeparator(). If a toolbar button is not
397 appropriate, a widget can be inserted instead using addWidget() or
398 insertWidget(). Examples of suitable widgets are QSpinBox,
399 QDoubleSpinBox, and QComboBox. When a toolbar button is pressed, it
400 emits the actionTriggered() signal.
401
402 A toolbar can be fixed in place in a particular area (e.g., at the
403 top of the window), or it can be movable between toolbar areas;
404 see setMovable(), isMovable(), allowedAreas() and isAreaAllowed().
405
406 When a toolbar is resized in such a way that it is too small to
407 show all the items it contains, an extension button will appear as
408 the last item in the toolbar. Pressing the extension button will
409 pop up a menu containing the items that do not currently fit in
410 the toolbar.
411
412 When a QToolBar is not a child of a QMainWindow, it loses the ability
413 to populate the extension pop up with widgets added to the toolbar using
414 addWidget(). Please use widget actions created by inheriting QWidgetAction
415 and implementing QWidgetAction::createWidget() instead.
416
417 \sa QToolButton, QMenu, QAction
418*/
419
420/*!
421 \fn bool QToolBar::isAreaAllowed(Qt::ToolBarArea area) const
422
423 Returns \c true if this toolbar is dockable in the given \a area;
424 otherwise returns \c false.
425*/
426
427/*!
428 \fn void QToolBar::actionTriggered(QAction *action)
429
430 This signal is emitted when an action in this toolbar is triggered.
431 This happens when the action's tool button is pressed, or when the
432 action is triggered in some other way outside the toolbar. The parameter
433 holds the triggered \a action.
434*/
435
436/*!
437 \fn void QToolBar::allowedAreasChanged(Qt::ToolBarAreas allowedAreas)
438
439 This signal is emitted when the collection of allowed areas for the
440 toolbar is changed. The new areas in which the toolbar can be positioned
441 are specified by \a allowedAreas.
442
443 \sa allowedAreas
444*/
445
446/*!
447 \fn void QToolBar::iconSizeChanged(const QSize &iconSize)
448
449 This signal is emitted when the icon size is changed. The \a
450 iconSize parameter holds the toolbar's new icon size.
451
452 \sa iconSize, QMainWindow::iconSize
453*/
454
455/*!
456 \fn void QToolBar::movableChanged(bool movable)
457
458 This signal is emitted when the toolbar becomes movable or fixed.
459 If the toolbar can be moved, \a movable is true; otherwise it is
460 false.
461
462 \sa movable
463*/
464
465/*!
466 \fn void QToolBar::orientationChanged(Qt::Orientation orientation)
467
468 This signal is emitted when the orientation of the toolbar changes.
469 The \a orientation parameter holds the toolbar's new orientation.
470
471 \sa orientation
472*/
473
474/*!
475 \fn void QToolBar::toolButtonStyleChanged(Qt::ToolButtonStyle toolButtonStyle)
476
477 This signal is emitted when the tool button style is changed. The
478 \a toolButtonStyle parameter holds the toolbar's new tool button
479 style.
480
481 \sa toolButtonStyle, QMainWindow::toolButtonStyle
482*/
483
484/*!
485 \since 4.6
486
487 \fn void QToolBar::topLevelChanged(bool topLevel)
488
489 This signal is emitted when the \l floating property changes.
490 The \a topLevel parameter is true if the toolbar is now floating;
491 otherwise it is false.
492
493 \sa isWindow()
494*/
495
496
497/*!
498 \fn void QToolBar::visibilityChanged(bool visible)
499 \since 4.7
500
501 This signal is emitted when the toolbar becomes \a visible (or
502 invisible). This happens when the widget is hidden or shown.
503*/
504
505/*!
506 Constructs a QToolBar with the given \a parent.
507*/
508QToolBar::QToolBar(QWidget *parent)
509 : QWidget(*new QToolBarPrivate, parent, { })
510{
511 Q_D(QToolBar);
512 d->init();
513}
514
515/*!
516 Constructs a QToolBar with the given \a parent.
517
518 The given window \a title identifies the toolbar and is shown in
519 the context menu provided by QMainWindow.
520
521 \sa setWindowTitle()
522*/
523QToolBar::QToolBar(const QString &title, QWidget *parent)
524 : QToolBar(parent)
525{
526 setWindowTitle(title);
527}
528
529
530/*!
531 Destroys the toolbar.
532*/
533QToolBar::~QToolBar()
534{
535}
536
537/*! \property QToolBar::movable
538 \brief whether the user can move the toolbar within the toolbar area,
539 or between toolbar areas.
540
541 By default, this property is \c true.
542
543 This property only makes sense if the toolbar is in a
544 QMainWindow.
545
546 \sa allowedAreas
547*/
548
549void QToolBar::setMovable(bool movable)
550{
551 Q_D(QToolBar);
552 if (!movable == !d->movable)
553 return;
554 d->movable = movable;
555 d->layout->invalidate();
556 emit movableChanged(movable: d->movable);
557}
558
559bool QToolBar::isMovable() const
560{
561 Q_D(const QToolBar);
562 return d->movable;
563}
564
565/*!
566 \property QToolBar::floatable
567 \brief whether the toolbar can be dragged and dropped as an independent window.
568
569 The default is true.
570*/
571bool QToolBar::isFloatable() const
572{
573 Q_D(const QToolBar);
574 return d->floatable;
575}
576
577void QToolBar::setFloatable(bool floatable)
578{
579 Q_D(QToolBar);
580 d->floatable = floatable;
581}
582
583/*!
584 \property QToolBar::floating
585 \brief whether the toolbar is an independent window.
586
587 By default, this property is \c true.
588
589 \sa QWidget::isWindow()
590*/
591bool QToolBar::isFloating() const
592{
593 return isWindow();
594}
595
596/*!
597 \property QToolBar::allowedAreas
598 \brief areas where the toolbar may be placed
599
600 The default is Qt::AllToolBarAreas.
601
602 This property only makes sense if the toolbar is in a
603 QMainWindow.
604
605 \sa movable
606*/
607
608void QToolBar::setAllowedAreas(Qt::ToolBarAreas areas)
609{
610 Q_D(QToolBar);
611 areas &= Qt::ToolBarArea_Mask;
612 if (areas == d->allowedAreas)
613 return;
614 d->allowedAreas = areas;
615 emit allowedAreasChanged(allowedAreas: d->allowedAreas);
616}
617
618Qt::ToolBarAreas QToolBar::allowedAreas() const
619{
620 Q_D(const QToolBar);
621 return d->allowedAreas;
622}
623
624/*! \property QToolBar::orientation
625 \brief orientation of the toolbar
626
627 The default is Qt::Horizontal.
628
629 This function should not be used when the toolbar is managed
630 by QMainWindow. You can use QMainWindow::addToolBar() or
631 QMainWindow::insertToolBar() if you wish to move a toolbar that
632 is already added to a main window to another Qt::ToolBarArea.
633*/
634
635void QToolBar::setOrientation(Qt::Orientation orientation)
636{
637 Q_D(QToolBar);
638 if (orientation == d->orientation)
639 return;
640
641 d->orientation = orientation;
642
643 if (orientation == Qt::Vertical)
644 setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred));
645 else
646 setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed));
647
648 d->layout->invalidate();
649 d->layout->activate();
650
651 emit orientationChanged(orientation: d->orientation);
652}
653
654Qt::Orientation QToolBar::orientation() const
655{ Q_D(const QToolBar); return d->orientation; }
656
657/*!
658 \property QToolBar::iconSize
659 \brief size of icons in the toolbar.
660
661 The default size is determined by the application's style and is
662 derived from the QStyle::PM_ToolBarIconSize pixel metric. It is
663 the maximum size an icon can have. Icons of smaller size will not
664 be scaled up.
665*/
666
667QSize QToolBar::iconSize() const
668{ Q_D(const QToolBar); return d->iconSize; }
669
670void QToolBar::setIconSize(const QSize &iconSize)
671{
672 Q_D(QToolBar);
673 QSize sz = iconSize;
674 if (!sz.isValid()) {
675 QMainWindow *mw = qobject_cast<QMainWindow *>(object: parentWidget());
676 if (mw && mw->layout()) {
677 QLayout *layout = mw->layout();
678 int i = 0;
679 QLayoutItem *item = nullptr;
680 do {
681 item = layout->itemAt(index: i++);
682 if (item && (item->widget() == this))
683 sz = mw->iconSize();
684 } while (!sz.isValid() && item != nullptr);
685 }
686 }
687 if (!sz.isValid()) {
688 const int metric = style()->pixelMetric(metric: QStyle::PM_ToolBarIconSize, option: nullptr, widget: this);
689 sz = QSize(metric, metric);
690 }
691 if (d->iconSize != sz) {
692 d->iconSize = sz;
693 setMinimumSize(minw: 0, minh: 0);
694 emit iconSizeChanged(iconSize: d->iconSize);
695 }
696 d->explicitIconSize = iconSize.isValid();
697
698 d->layout->invalidate();
699}
700
701/*!
702 \property QToolBar::toolButtonStyle
703 \brief the style of toolbar buttons
704
705 This property defines the style of all tool buttons that are added
706 as \l{QAction}s. Note that if you add a QToolButton with the
707 addWidget() method, it will not get this button style.
708
709 To have the style of toolbuttons follow the system settings, set this property to Qt::ToolButtonFollowStyle.
710 On Unix, the user settings from the desktop environment will be used.
711 On other platforms, Qt::ToolButtonFollowStyle means icon only.
712
713 The default is Qt::ToolButtonIconOnly.
714*/
715
716Qt::ToolButtonStyle QToolBar::toolButtonStyle() const
717{ Q_D(const QToolBar); return d->toolButtonStyle; }
718
719void QToolBar::setToolButtonStyle(Qt::ToolButtonStyle toolButtonStyle)
720{
721 Q_D(QToolBar);
722 d->explicitToolButtonStyle = true;
723 if (d->toolButtonStyle == toolButtonStyle)
724 return;
725 d->toolButtonStyle = toolButtonStyle;
726 setMinimumSize(minw: 0, minh: 0);
727 emit toolButtonStyleChanged(toolButtonStyle: d->toolButtonStyle);
728}
729
730/*!
731 Removes all actions from the toolbar.
732
733 \sa removeAction()
734*/
735void QToolBar::clear()
736{
737 QList<QAction *> actions = this->actions();
738 for(int i = 0; i < actions.size(); i++)
739 removeAction(action: actions.at(i));
740}
741
742/*!
743 Adds a separator to the end of the toolbar.
744
745 \sa insertSeparator()
746*/
747QAction *QToolBar::addSeparator()
748{
749 QAction *action = new QAction(this);
750 action->setSeparator(true);
751 addAction(action);
752 return action;
753}
754
755/*!
756 Inserts a separator into the toolbar in front of the toolbar
757 item associated with the \a before action.
758
759 \sa addSeparator()
760*/
761QAction *QToolBar::insertSeparator(QAction *before)
762{
763 QAction *action = new QAction(this);
764 action->setSeparator(true);
765 insertAction(before, action);
766 return action;
767}
768
769/*!
770 Adds the given \a widget to the toolbar as the toolbar's last
771 item.
772
773 The toolbar takes ownership of \a widget.
774
775 If you add a QToolButton with this method, the toolbar's
776 Qt::ToolButtonStyle will not be respected.
777
778 \note You should use QAction::setVisible() to change the
779 visibility of the widget. Using QWidget::setVisible(),
780 QWidget::show() and QWidget::hide() does not work.
781
782 \sa insertWidget()
783*/
784QAction *QToolBar::addWidget(QWidget *widget)
785{
786 QWidgetAction *action = new QWidgetAction(this);
787 action->setDefaultWidget(widget);
788 action->d_func()->autoCreated = true;
789 addAction(action);
790 return action;
791}
792
793/*!
794 Inserts the given \a widget in front of the toolbar item
795 associated with the \a before action.
796
797 Note: You should use QAction::setVisible() to change the
798 visibility of the widget. Using QWidget::setVisible(),
799 QWidget::show() and QWidget::hide() does not work.
800
801 \sa addWidget()
802*/
803QAction *QToolBar::insertWidget(QAction *before, QWidget *widget)
804{
805 QWidgetAction *action = new QWidgetAction(this);
806 action->setDefaultWidget(widget);
807 action->d_func()->autoCreated = true;
808 insertAction(before, action);
809 return action;
810}
811
812/*!
813 \internal
814
815 Returns the geometry of the toolbar item associated with the given
816 \a action, or an invalid QRect if no matching item is found.
817*/
818QRect QToolBar::actionGeometry(QAction *action) const
819{
820 Q_D(const QToolBar);
821
822 int index = d->layout->indexOf(action);
823 if (index == -1)
824 return QRect();
825 return d->layout->itemAt(index)->widget()->geometry();
826}
827
828/*!
829 Returns the action at point \a p. This function returns zero if no
830 action was found.
831
832 \sa QWidget::childAt()
833*/
834QAction *QToolBar::actionAt(const QPoint &p) const
835{
836 Q_D(const QToolBar);
837 QWidget *widget = childAt(p);
838 int index = d->layout->indexOf(widget);
839 if (index == -1)
840 return nullptr;
841 QLayoutItem *item = d->layout->itemAt(index);
842 return static_cast<QToolBarItem*>(item)->action;
843}
844
845/*! \fn QAction *QToolBar::actionAt(int x, int y) const
846 \overload
847
848 Returns the action at the point \a x, \a y. This function returns
849 zero if no action was found.
850*/
851
852/*! \reimp */
853void QToolBar::actionEvent(QActionEvent *event)
854{
855 Q_D(QToolBar);
856 auto action = static_cast<QAction *>(event->action());
857 QWidgetAction *widgetAction = qobject_cast<QWidgetAction *>(object: action);
858
859 switch (event->type()) {
860 case QEvent::ActionAdded: {
861 Q_ASSERT_X(widgetAction == nullptr || d->layout->indexOf(widgetAction) == -1,
862 "QToolBar", "widgets cannot be inserted multiple times");
863
864 // reparent the action to this toolbar if it has been created
865 // using the addAction(text) etc. convenience functions, to
866 // preserve Qt 4.1.x behavior. The widget is already
867 // reparented to us due to the createWidget call inside
868 // createItem()
869 if (widgetAction != nullptr && widgetAction->d_func()->autoCreated)
870 widgetAction->setParent(this);
871
872 int index = d->layout->count();
873 if (event->before()) {
874 index = d->layout->indexOf(action: event->before());
875 Q_ASSERT_X(index != -1, "QToolBar::insertAction", "internal error");
876 }
877 d->layout->insertAction(index, action);
878 break;
879 }
880
881 case QEvent::ActionChanged:
882 d->layout->invalidate();
883 break;
884
885 case QEvent::ActionRemoved: {
886 int index = d->layout->indexOf(action);
887 if (index != -1) {
888 delete d->layout->takeAt(index);
889 }
890 break;
891 }
892
893 default:
894 Q_ASSERT_X(false, "QToolBar::actionEvent", "internal error");
895 }
896}
897
898/*! \reimp */
899void QToolBar::changeEvent(QEvent *event)
900{
901 Q_D(QToolBar);
902 switch (event->type()) {
903 case QEvent::WindowTitleChange:
904 d->toggleViewAction->setText(windowTitle());
905 break;
906 case QEvent::StyleChange:
907 d->layout->invalidate();
908 if (!d->explicitIconSize) {
909 QStyleOptionToolBar opt;
910 initStyleOption(option: &opt);
911 const int metric = style()->pixelMetric(metric: QStyle::PM_ToolBarIconSize, option: &opt, widget: this);
912 setIconSize({metric, metric});
913 d->explicitIconSize = false;
914 }
915 d->layout->updateMarginAndSpacing();
916 break;
917 case QEvent::LayoutDirectionChange:
918 d->layout->invalidate();
919 break;
920 default:
921 break;
922 }
923 QWidget::changeEvent(event);
924}
925
926/*! \reimp */
927void QToolBar::paintEvent(QPaintEvent *)
928{
929 Q_D(QToolBar);
930
931 QPainter p(this);
932 QStyle *style = this->style();
933 QStyleOptionToolBar opt;
934 initStyleOption(option: &opt);
935
936 if (d->layout->expanded || d->layout->animating || isWindow()) {
937 //if the toolbar is expended, we need to fill the background with the window color
938 //because some styles may expects that.
939 p.fillRect(opt.rect, palette().window());
940 style->drawControl(element: QStyle::CE_ToolBar, opt: &opt, p: &p, w: this);
941 style->drawPrimitive(pe: QStyle::PE_FrameMenu, opt: &opt, p: &p, w: this);
942 } else {
943 style->drawControl(element: QStyle::CE_ToolBar, opt: &opt, p: &p, w: this);
944 }
945
946 opt.rect = style->subElementRect(subElement: QStyle::SE_ToolBarHandle, option: &opt, widget: this);
947 if (opt.rect.isValid())
948 style->drawPrimitive(pe: QStyle::PE_IndicatorToolBarHandle, opt: &opt, p: &p, w: this);
949}
950
951/*
952 Checks if an expanded toolbar has to wait for this popup to close before
953 the toolbar collapses. This is true if
954 1) the popup has the toolbar in its parent chain,
955 2) the popup is a menu whose menuAction is somewhere in the toolbar.
956*/
957static bool waitForPopup(QToolBar *tb, QWidget *popup)
958{
959 if (popup == nullptr || popup->isHidden())
960 return false;
961
962 QWidget *w = popup;
963 while (w != nullptr) {
964 if (w == tb)
965 return true;
966 w = w->parentWidget();
967 }
968
969 QMenu *menu = qobject_cast<QMenu*>(object: popup);
970 if (menu == nullptr)
971 return false;
972
973 const QAction *action = menu->menuAction();
974 for (auto object : action->associatedObjects()) {
975 if (QWidget *widget = qobject_cast<QWidget*>(o: object)) {
976 if (waitForPopup(tb, popup: widget))
977 return true;
978 }
979 }
980
981 return false;
982}
983
984#ifdef Q_OS_MACOS
985static void enableMacToolBar(QToolBar *toolbar, bool enable)
986{
987 QPlatformNativeInterface *nativeInterface = QApplication::platformNativeInterface();
988 if (!nativeInterface)
989 return;
990 QPlatformNativeInterface::NativeResourceForIntegrationFunction function =
991 nativeInterface->nativeResourceFunctionForIntegration("setContentBorderAreaEnabled");
992 if (!function)
993 return; // Not Cocoa platform plugin.
994
995 typedef void (*SetContentBorderAreaEnabledFunction)(QWindow *window, void *identifier, bool enabled);
996 (reinterpret_cast<SetContentBorderAreaEnabledFunction>(QFunctionPointer(function)))(
997 toolbar->window()->windowHandle(), toolbar, enable);
998}
999#endif
1000
1001
1002/*! \reimp */
1003bool QToolBar::event(QEvent *event)
1004{
1005 Q_D(QToolBar);
1006
1007 switch (event->type()) {
1008 case QEvent::Timer:
1009 if (d->waitForPopupTimer.timerId() == static_cast<QTimerEvent*>(event)->timerId()) {
1010 QWidget *w = QApplication::activePopupWidget();
1011 if (!waitForPopup(tb: this, popup: w)) {
1012 d->waitForPopupTimer.stop();
1013 if (!this->underMouse())
1014 d->layout->setExpanded(false);
1015 }
1016 }
1017 break;
1018 case QEvent::Hide:
1019 if (!isHidden())
1020 break;
1021 Q_FALLTHROUGH();
1022 case QEvent::Show:
1023 d->toggleViewAction->setChecked(event->type() == QEvent::Show);
1024#ifdef Q_OS_MACOS
1025 enableMacToolBar(this, event->type() == QEvent::Show);
1026#endif
1027 emit visibilityChanged(visible: event->type() == QEvent::Show);
1028 break;
1029 case QEvent::ParentChange:
1030 d->layout->checkUsePopupMenu();
1031 break;
1032
1033 case QEvent::MouseButtonPress: {
1034 if (d->mousePressEvent(event: static_cast<QMouseEvent*>(event)))
1035 return true;
1036 break;
1037 }
1038 case QEvent::MouseButtonRelease:
1039 if (d->mouseReleaseEvent(static_cast<QMouseEvent*>(event)))
1040 return true;
1041 break;
1042 case QEvent::HoverEnter:
1043 case QEvent::HoverLeave:
1044 // there's nothing special to do here and we don't want to update the whole widget
1045 return true;
1046 case QEvent::HoverMove: {
1047#ifndef QT_NO_CURSOR
1048 QHoverEvent *e = static_cast<QHoverEvent*>(event);
1049 QStyleOptionToolBar opt;
1050 initStyleOption(option: &opt);
1051 if (style()->subElementRect(subElement: QStyle::SE_ToolBarHandle, option: &opt, widget: this).contains(p: e->position().toPoint()))
1052 setCursor(Qt::SizeAllCursor);
1053 else
1054 unsetCursor();
1055#endif
1056 break;
1057 }
1058 case QEvent::MouseMove:
1059 if (d->mouseMoveEvent(event: static_cast<QMouseEvent*>(event)))
1060 return true;
1061 break;
1062 case QEvent::Leave:
1063 if (d->state != nullptr && d->state->dragging) {
1064#ifdef Q_OS_WIN
1065 // This is a workaround for losing the mouse on Vista.
1066 QPoint pos = QCursor::pos();
1067 QMouseEvent fake(QEvent::MouseMove, mapFromGlobal(pos), pos, Qt::NoButton,
1068 QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers());
1069 d->mouseMoveEvent(&fake);
1070#endif
1071 } else {
1072 if (!d->layout->expanded)
1073 break;
1074
1075 QWidget *w = QApplication::activePopupWidget();
1076 if (waitForPopup(tb: this, popup: w)) {
1077 d->waitForPopupTimer.start(POPUP_TIMER_INTERVAL, obj: this);
1078 break;
1079 }
1080
1081 d->waitForPopupTimer.stop();
1082 d->layout->setExpanded(false);
1083 break;
1084 }
1085 break;
1086 default:
1087 break;
1088 }
1089 return QWidget::event(event);
1090}
1091
1092/*!
1093 Returns a checkable action that can be used to show or hide this
1094 toolbar.
1095
1096 The action's text is set to the toolbar's window title.
1097
1098 \sa QAction::text, QWidget::windowTitle
1099*/
1100QAction *QToolBar::toggleViewAction() const
1101{ Q_D(const QToolBar); return d->toggleViewAction; }
1102
1103/*!
1104 \since 4.2
1105
1106 Returns the widget associated with the specified \a action.
1107
1108 \sa addWidget()
1109*/
1110QWidget *QToolBar::widgetForAction(QAction *action) const
1111{
1112 Q_D(const QToolBar);
1113
1114 int index = d->layout->indexOf(action);
1115 if (index == -1)
1116 return nullptr;
1117
1118 return d->layout->itemAt(index)->widget();
1119}
1120
1121extern QMainWindowLayout *qt_mainwindow_layout(const QMainWindow *window);
1122
1123/*!
1124 \internal
1125*/
1126void QToolBar::initStyleOption(QStyleOptionToolBar *option) const
1127{
1128 Q_D(const QToolBar);
1129
1130 if (!option)
1131 return;
1132
1133 option->initFrom(w: this);
1134 if (orientation() == Qt::Horizontal)
1135 option->state |= QStyle::State_Horizontal;
1136 option->lineWidth = style()->pixelMetric(metric: QStyle::PM_ToolBarFrameWidth, option: nullptr, widget: this);
1137 option->features = d->layout->movable()
1138 ? QStyleOptionToolBar::Movable
1139 : QStyleOptionToolBar::None;
1140 // if the tool bar is not in a QMainWindow, this will make the painting right
1141 option->toolBarArea = Qt::NoToolBarArea;
1142
1143 // Add more styleoptions if the toolbar has been added to a mainwindow.
1144 QMainWindow *mainWindow = qobject_cast<QMainWindow *>(object: parentWidget());
1145
1146 if (!mainWindow)
1147 return;
1148
1149 QMainWindowLayout *layout = qt_mainwindow_layout(window: mainWindow);
1150 Q_ASSERT_X(layout != nullptr, "QToolBar::initStyleOption()",
1151 "QMainWindow->layout() != QMainWindowLayout");
1152
1153 layout->getStyleOptionInfo(option, toolBar: const_cast<QToolBar *>(this));
1154}
1155
1156QT_END_NAMESPACE
1157
1158#include "moc_qtoolbar.cpp"
1159

source code of qtbase/src/widgets/widgets/qtoolbar.cpp