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

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