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