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