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

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