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(QInternal::KeepSavedState);
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 auto evPos = event->position().toPoint();
313 const bool moving = !q->isWindow() && (orientation == Qt::Vertical ?
314 evPos.x() >= 0 && evPos.x() < q->width() :
315 evPos.y() >= 0 && evPos.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(state->widgetItem, 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(event->globalPosition(), handle).toPoint()
347 - QHighDpi::fromNativePixels(globalPressPos, 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(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(q->mapToGlobal(QPoint(0, 0)));
370 setWindowState(true, true, r);
371 layout->setExpanded(false);
372}
373
374void QToolBarPrivate::plug(const QRect &r)
375{
376 setWindowState(false, false, 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(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(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(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 *>(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(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(QStyle::PM_ToolBarIconSize, nullptr, this);
691 sz = QSize(metric, metric);
692 }
693 if (d->iconSize != sz) {
694 d->iconSize = sz;
695 setMinimumSize(0, 0);
696 emit iconSizeChanged(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(0, 0);
729 emit toolButtonStyleChanged(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(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 *>(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(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 QStyleOptionToolBar opt;
912 initStyleOption(&opt);
913 const int metric = style()->pixelMetric(QStyle::PM_ToolBarIconSize, &opt, this);
914 setIconSize({metric, metric});
915 d->explicitIconSize = false;
916 }
917 d->layout->updateMarginAndSpacing();
918 break;
919 case QEvent::LayoutDirectionChange:
920 d->layout->invalidate();
921 break;
922 default:
923 break;
924 }
925 QWidget::changeEvent(event);
926}
927
928/*! \reimp */
929void QToolBar::paintEvent(QPaintEvent *)
930{
931 Q_D(QToolBar);
932
933 QPainter p(this);
934 QStyle *style = this->style();
935 QStyleOptionToolBar opt;
936 initStyleOption(&opt);
937
938 if (d->layout->expanded || d->layout->animating || isWindow()) {
939 //if the toolbar is expended, we need to fill the background with the window color
940 //because some styles may expects that.
941 p.fillRect(opt.rect, palette().window());
942 style->drawControl(QStyle::CE_ToolBar, &opt, &p, this);
943 style->drawPrimitive(QStyle::PE_FrameMenu, &opt, &p, this);
944 } else {
945 style->drawControl(QStyle::CE_ToolBar, &opt, &p, this);
946 }
947
948 opt.rect = style->subElementRect(QStyle::SE_ToolBarHandle, &opt, this);
949 if (opt.rect.isValid())
950 style->drawPrimitive(QStyle::PE_IndicatorToolBarHandle, &opt, &p, this);
951}
952
953/*
954 Checks if an expanded toolbar has to wait for this popup to close before
955 the toolbar collapses. This is true if
956 1) the popup has the toolbar in its parent chain,
957 2) the popup is a menu whose menuAction is somewhere in the toolbar.
958*/
959static bool waitForPopup(QToolBar *tb, QWidget *popup)
960{
961 if (popup == nullptr || popup->isHidden())
962 return false;
963
964 QWidget *w = popup;
965 while (w != nullptr) {
966 if (w == tb)
967 return true;
968 w = w->parentWidget();
969 }
970
971 QMenu *menu = qobject_cast<QMenu*>(popup);
972 if (menu == nullptr)
973 return false;
974
975 const QAction *action = menu->menuAction();
976 for (auto object : action->associatedObjects()) {
977 if (QWidget *widget = qobject_cast<QWidget*>(object)) {
978 if (waitForPopup(tb, widget))
979 return true;
980 }
981 }
982
983 return false;
984}
985
986#ifdef Q_OS_MACOS
987static void enableMacToolBar(QToolBar *toolbar, bool enable)
988{
989 QPlatformNativeInterface *nativeInterface = QApplication::platformNativeInterface();
990 if (!nativeInterface)
991 return;
992 QPlatformNativeInterface::NativeResourceForIntegrationFunction function =
993 nativeInterface->nativeResourceFunctionForIntegration("setContentBorderAreaEnabled");
994 if (!function)
995 return; // Not Cocoa platform plugin.
996
997 typedef void (*SetContentBorderAreaEnabledFunction)(QWindow *window, void *identifier, bool enabled);
998 (reinterpret_cast<SetContentBorderAreaEnabledFunction>(QFunctionPointer(function)))(
999 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(this, 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(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(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(&opt);
1053 if (style()->subElementRect(QStyle::SE_ToolBarHandle, &opt, this).contains(e->position().toPoint()))
1054 setCursor(Qt::SizeAllCursor);
1055 else
1056 unsetCursor();
1057#endif
1058 break;
1059 }
1060 case QEvent::MouseMove:
1061 if (d->mouseMoveEvent(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(this, w)) {
1079 d->waitForPopupTimer.start(POPUP_TIMER_INTERVAL, 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(this);
1136 if (orientation() == Qt::Horizontal)
1137 option->state |= QStyle::State_Horizontal;
1138 option->lineWidth = style()->pixelMetric(QStyle::PM_ToolBarFrameWidth, nullptr, 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 *>(parentWidget());
1147
1148 if (!mainWindow)
1149 return;
1150
1151 QMainWindowLayout *layout = qt_mainwindow_layout(mainWindow);
1152 Q_ASSERT_X(layout != nullptr, "QToolBar::initStyleOption()",
1153 "QMainWindow->layout() != QMainWindowLayout");
1154
1155 layout->getStyleOptionInfo(option, const_cast<QToolBar *>(this));
1156}
1157
1158QT_END_NAMESPACE
1159
1160#include "moc_qtoolbar.cpp"
bool explicitToolButtonStyle
Definition qtoolbar_p.h:53
void plug(const QRect &r)
Definition qtoolbar.cpp:374
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:365
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
Combined button and popup list for selecting options.
QMainWindowLayout * qt_mainwindow_layout(const QMainWindow *window)
static bool waitForPopup(QToolBar *tb, QWidget *popup)
Definition qtoolbar.cpp:959
#define POPUP_TIMER_INTERVAL
Definition qtoolbar.cpp:34