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