VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/widgets/UIMiniToolBar.cpp@ 82781

Last change on this file since 82781 was 80955, checked in by vboxsync, 5 years ago

FE/Qt: bugref:8938. Changing connection syntax under the widget subfolder.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.3 KB
Line 
1/* $Id: UIMiniToolBar.cpp 80955 2019-09-23 17:27:06Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIMiniToolBar class implementation.
4 */
5
6/*
7 * Copyright (C) 2009-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Qt includes: */
19#include <QApplication>
20#include <QLabel>
21#include <QMenu>
22#include <QMoveEvent>
23#include <QPainter>
24#include <QStateMachine>
25#include <QStyle>
26#include <QTimer>
27#include <QToolButton>
28#include <QVBoxLayout>
29#include <QWindow>
30#ifdef VBOX_WS_X11
31# include <QWindowStateChangeEvent>
32#endif
33
34/* GUI includes: */
35#include "UIMiniToolBar.h"
36#include "UIAnimationFramework.h"
37#include "UIIconPool.h"
38#include "UIDesktopWidgetWatchdog.h"
39#include "UICommon.h"
40#ifdef VBOX_WS_X11
41# include "UIExtraDataManager.h"
42#endif
43
44
45/** UIToolBar reimplementation
46 * providing UIMiniToolBar with mini-toolbar. */
47class UIMiniToolBarPrivate : public UIToolBar
48{
49 Q_OBJECT;
50
51signals:
52
53 /** Notifies listeners about we are resized. */
54 void sigResized();
55
56 /** Notifies listeners about action triggered to toggle auto-hide. */
57 void sigAutoHideToggled();
58 /** Notifies listeners about action triggered to minimize. */
59 void sigMinimizeAction();
60 /** Notifies listeners about action triggered to exit. */
61 void sigExitAction();
62 /** Notifies listeners about action triggered to close. */
63 void sigCloseAction();
64
65public:
66
67 /** Constructor. */
68 UIMiniToolBarPrivate();
69
70 /** Defines @a alignment. */
71 void setAlignment(Qt::Alignment alignment);
72
73 /** Returns whether we do auto-hide. */
74 bool autoHide() const;
75 /** Defines whether we do @a fAutoHide. */
76 void setAutoHide(bool fAutoHide);
77
78 /** Defines our @a strText. */
79 void setText(const QString &strText);
80
81 /** Adds our @a menus. */
82 void addMenus(const QList<QMenu*> &menus);
83
84protected:
85
86 /** Show @a pEvent handler. */
87 virtual void showEvent(QShowEvent *pEvent);
88 /** Polish @a pEvent handler. */
89 virtual void polishEvent(QShowEvent *pEvent);
90 /** Resize @a pEvent handler. */
91 virtual void resizeEvent(QResizeEvent *pEvent);
92 /** Paint @a pEvent handler. */
93 virtual void paintEvent(QPaintEvent *pEvent);
94
95private:
96
97 /** Prepare routine. */
98 void prepare();
99
100 /** Rebuilds our shape. */
101 void rebuildShape();
102
103 /** Holds whether this widget was polished. */
104 bool m_fPolished;
105 /** Holds the alignment type. */
106 Qt::Alignment m_alignment;
107 /** Holds the shape. */
108 QPainterPath m_shape;
109
110 /** Holds the action to toggle auto-hide. */
111 QAction *m_pAutoHideAction;
112 /** Holds the name label. */
113 QLabel *m_pLabel;
114 /** Holds the action to trigger minimize. */
115 QAction *m_pMinimizeAction;
116 /** Holds the action to trigger exit. */
117 QAction *m_pRestoreAction;
118 /** Holds the action to trigger close. */
119 QAction *m_pCloseAction;
120
121 /** Holds the pointer to the place to insert menu. */
122 QAction *m_pMenuInsertPosition;
123
124 /** Holds the spacings. */
125 QList<QWidget*> m_spacings;
126 /** Holds the margins. */
127 QList<QWidget*> m_margins;
128};
129
130
131/*********************************************************************************************************************************
132* Class UIMiniToolBarPrivate implementation. *
133*********************************************************************************************************************************/
134
135UIMiniToolBarPrivate::UIMiniToolBarPrivate()
136 /* Variables: General stuff: */
137 : m_fPolished(false)
138 , m_alignment(Qt::AlignBottom)
139 /* Variables: Contents stuff: */
140 , m_pAutoHideAction(0)
141 , m_pLabel(0)
142 , m_pMinimizeAction(0)
143 , m_pRestoreAction(0)
144 , m_pCloseAction(0)
145 /* Variables: Menu stuff: */
146 , m_pMenuInsertPosition(0)
147{
148 /* Prepare: */
149 prepare();
150}
151
152void UIMiniToolBarPrivate::setAlignment(Qt::Alignment alignment)
153{
154 /* Make sure alignment really changed: */
155 if (m_alignment == alignment)
156 return;
157
158 /* Update alignment: */
159 m_alignment = alignment;
160
161 /* Rebuild shape: */
162 rebuildShape();
163}
164
165bool UIMiniToolBarPrivate::autoHide() const
166{
167 /* Return auto-hide: */
168 return !m_pAutoHideAction->isChecked();
169}
170
171void UIMiniToolBarPrivate::setAutoHide(bool fAutoHide)
172{
173 /* Make sure auto-hide really changed: */
174 if (m_pAutoHideAction->isChecked() == !fAutoHide)
175 return;
176
177 /* Update auto-hide: */
178 m_pAutoHideAction->setChecked(!fAutoHide);
179}
180
181void UIMiniToolBarPrivate::setText(const QString &strText)
182{
183 /* Make sure text really changed: */
184 if (m_pLabel->text() == strText)
185 return;
186
187 /* Update text: */
188 m_pLabel->setText(strText);
189
190 /* Resize to sizehint: */
191 resize(sizeHint());
192}
193
194void UIMiniToolBarPrivate::addMenus(const QList<QMenu*> &menus)
195{
196 /* For each of the passed menu items: */
197 for (int i = 0; i < menus.size(); ++i)
198 {
199 /* Get corresponding menu-action: */
200 QAction *pAction = menus[i]->menuAction();
201 /* Insert it into corresponding place: */
202 insertAction(m_pMenuInsertPosition, pAction);
203 /* Configure corresponding tool-button: */
204 if (QToolButton *pButton = qobject_cast<QToolButton*>(widgetForAction(pAction)))
205 {
206 pButton->setPopupMode(QToolButton::InstantPopup);
207 pButton->setAutoRaise(true);
208 }
209 /* Add some spacing: */
210 if (i != menus.size() - 1)
211 m_spacings << widgetForAction(insertWidget(m_pMenuInsertPosition, new QWidget(this)));
212 }
213
214 /* Resize to sizehint: */
215 resize(sizeHint());
216}
217
218void UIMiniToolBarPrivate::showEvent(QShowEvent *pEvent)
219{
220 /* Make sure we should polish dialog: */
221 if (m_fPolished)
222 return;
223
224 /* Call to polish-event: */
225 polishEvent(pEvent);
226
227 /* Mark dialog as polished: */
228 m_fPolished = true;
229}
230
231void UIMiniToolBarPrivate::polishEvent(QShowEvent*)
232{
233 /* Toolbar spacings: */
234 foreach(QWidget *pSpacing, m_spacings)
235 pSpacing->setMinimumWidth(5);
236
237 /* Title spacings: */
238 foreach(QWidget *pLableMargin, m_margins)
239 pLableMargin->setMinimumWidth(15);
240
241 /* Resize to sizehint: */
242 resize(sizeHint());
243}
244
245void UIMiniToolBarPrivate::resizeEvent(QResizeEvent*)
246{
247 /* Rebuild shape: */
248 rebuildShape();
249
250 /* Notify listeners: */
251 emit sigResized();
252}
253
254void UIMiniToolBarPrivate::paintEvent(QPaintEvent*)
255{
256 /* Prepare painter: */
257 QPainter painter(this);
258
259 /* Fill background: */
260 if (!m_shape.isEmpty())
261 {
262 painter.setRenderHint(QPainter::Antialiasing);
263 painter.setClipPath(m_shape);
264 }
265 QRect backgroundRect = rect();
266 QColor backgroundColor = palette().color(QPalette::Window);
267 QLinearGradient headerGradient(backgroundRect.bottomLeft(), backgroundRect.topLeft());
268 headerGradient.setColorAt(0, backgroundColor.darker(120));
269 headerGradient.setColorAt(1, backgroundColor.darker(90));
270 painter.fillRect(backgroundRect, headerGradient);
271}
272
273void UIMiniToolBarPrivate::prepare()
274{
275 /* Determine icon metric: */
276 const QStyle *pStyle = QApplication::style();
277 const int iIconMetric = pStyle->pixelMetric(QStyle::PM_SmallIconSize);
278
279 /* Configure toolbar: */
280 setIconSize(QSize(iIconMetric, iIconMetric));
281
282 /* Left margin: */
283#ifdef VBOX_WS_X11
284 if (uiCommon().isCompositingManagerRunning())
285 m_spacings << widgetForAction(addWidget(new QWidget));
286#else /* !VBOX_WS_X11 */
287 m_spacings << widgetForAction(addWidget(new QWidget));
288#endif /* !VBOX_WS_X11 */
289
290 /* Prepare push-pin: */
291 m_pAutoHideAction = new QAction(this);
292 m_pAutoHideAction->setIcon(UIIconPool::iconSet(":/pin_16px.png"));
293 m_pAutoHideAction->setToolTip(UIMiniToolBar::tr("Always show the toolbar"));
294 m_pAutoHideAction->setCheckable(true);
295 connect(m_pAutoHideAction, SIGNAL(toggled(bool)), this, SIGNAL(sigAutoHideToggled()));
296 addAction(m_pAutoHideAction);
297
298 /* Left menu margin: */
299 m_spacings << widgetForAction(addWidget(new QWidget));
300
301 /* Right menu margin: */
302 m_pMenuInsertPosition = addWidget(new QWidget);
303 m_spacings << widgetForAction(m_pMenuInsertPosition);
304
305 /* Left label margin: */
306 m_margins << widgetForAction(addWidget(new QWidget));
307
308 /* Insert a label for VM Name: */
309 m_pLabel = new QLabel;
310 m_pLabel->setAlignment(Qt::AlignCenter);
311 addWidget(m_pLabel);
312
313 /* Right label margin: */
314 m_margins << widgetForAction(addWidget(new QWidget));
315
316 /* Minimize action: */
317 m_pMinimizeAction = new QAction(this);
318 m_pMinimizeAction->setIcon(UIIconPool::iconSet(":/minimize_16px.png"));
319 m_pMinimizeAction->setToolTip(UIMiniToolBar::tr("Minimize Window"));
320 connect(m_pMinimizeAction, SIGNAL(triggered()), this, SIGNAL(sigMinimizeAction()));
321 addAction(m_pMinimizeAction);
322
323 /* Exit action: */
324 m_pRestoreAction = new QAction(this);
325 m_pRestoreAction->setIcon(UIIconPool::iconSet(":/restore_16px.png"));
326 m_pRestoreAction->setToolTip(UIMiniToolBar::tr("Exit Full Screen or Seamless Mode"));
327 connect(m_pRestoreAction, SIGNAL(triggered()), this, SIGNAL(sigExitAction()));
328 addAction(m_pRestoreAction);
329
330 /* Close action: */
331 m_pCloseAction = new QAction(this);
332 m_pCloseAction->setIcon(UIIconPool::iconSet(":/close_16px.png"));
333 m_pCloseAction->setToolTip(UIMiniToolBar::tr("Close VM"));
334 connect(m_pCloseAction, SIGNAL(triggered()), this, SIGNAL(sigCloseAction()));
335 addAction(m_pCloseAction);
336
337 /* Right margin: */
338#ifdef VBOX_WS_X11
339 if (uiCommon().isCompositingManagerRunning())
340 m_spacings << widgetForAction(addWidget(new QWidget));
341#else /* !VBOX_WS_X11 */
342 m_spacings << widgetForAction(addWidget(new QWidget));
343#endif /* !VBOX_WS_X11 */
344}
345
346void UIMiniToolBarPrivate::rebuildShape()
347{
348#ifdef VBOX_WS_X11
349 if (!uiCommon().isCompositingManagerRunning())
350 return;
351#endif /* VBOX_WS_X11 */
352
353 /* Rebuild shape: */
354 QPainterPath shape;
355 switch (m_alignment)
356 {
357 case Qt::AlignTop:
358 {
359 shape.moveTo(0, 0);
360 shape.lineTo(shape.currentPosition().x(), height() - 10);
361 shape.arcTo(QRectF(shape.currentPosition(), QSizeF(20, 20)).translated(0, -10), 180, 90);
362 shape.lineTo(width() - 10, shape.currentPosition().y());
363 shape.arcTo(QRectF(shape.currentPosition(), QSizeF(20, 20)).translated(-10, -20), 270, 90);
364 shape.lineTo(shape.currentPosition().x(), 0);
365 shape.closeSubpath();
366 break;
367 }
368 case Qt::AlignBottom:
369 {
370 shape.moveTo(0, height());
371 shape.lineTo(shape.currentPosition().x(), 10);
372 shape.arcTo(QRectF(shape.currentPosition(), QSizeF(20, 20)).translated(0, -10), 180, -90);
373 shape.lineTo(width() - 10, shape.currentPosition().y());
374 shape.arcTo(QRectF(shape.currentPosition(), QSizeF(20, 20)).translated(-10, 0), 90, -90);
375 shape.lineTo(shape.currentPosition().x(), height());
376 shape.closeSubpath();
377 break;
378 }
379 default:
380 break;
381 }
382 m_shape = shape;
383
384 /* Update: */
385 update();
386}
387
388
389/*********************************************************************************************************************************
390* Class UIMiniToolBar implementation. *
391*********************************************************************************************************************************/
392
393/* static */
394Qt::WindowFlags UIMiniToolBar::defaultWindowFlags(GeometryType geometryType)
395{
396 /* Not everywhere: */
397 Q_UNUSED(geometryType);
398
399#ifdef VBOX_WS_X11
400 /* Depending on current WM: */
401 switch (uiCommon().typeOfWindowManager())
402 {
403 // WORKAROUND:
404 // By strange reason, frameless full-screen windows under certain WMs
405 // do not respect the transient relationship between each other.
406 // By nor less strange reason, frameless full-screen *tool* windows
407 // respects such relationship, so we are doing what WM want.
408 case X11WMType_GNOMEShell:
409 case X11WMType_KWin:
410 case X11WMType_Metacity:
411 case X11WMType_Mutter:
412 case X11WMType_Xfwm4:
413 return geometryType == GeometryType_Full ?
414 Qt::Tool | Qt::FramelessWindowHint :
415 Qt::Window | Qt::FramelessWindowHint;
416 default: break;
417 }
418#endif /* VBOX_WS_X11 */
419
420 /* Frameless window by default: */
421 return Qt::Window | Qt::FramelessWindowHint;
422}
423
424UIMiniToolBar::UIMiniToolBar(QWidget *pParent,
425 GeometryType geometryType,
426 Qt::Alignment alignment,
427 bool fAutoHide /* = true */,
428 int iWindowIndex /* = -1 */)
429 : QWidget(0, defaultWindowFlags(geometryType))
430 /* Variables: General stuff: */
431 , m_pParent(pParent)
432 , m_geometryType(geometryType)
433 , m_alignment(alignment)
434 , m_fAutoHide(fAutoHide)
435 , m_iWindowIndex(iWindowIndex)
436 /* Variables: Contents stuff: */
437 , m_pArea(0)
438 , m_pToolbar(0)
439 /* Variables: Hover stuff: */
440 , m_fHovered(false)
441 , m_pHoverEnterTimer(0)
442 , m_pHoverLeaveTimer(0)
443 , m_pAnimation(0)
444#ifdef VBOX_WS_X11
445 , m_fIsParentMinimized(false)
446#endif
447{
448 /* Prepare: */
449 prepare();
450}
451
452UIMiniToolBar::~UIMiniToolBar()
453{
454 /* Cleanup: */
455 cleanup();
456}
457
458void UIMiniToolBar::setAlignment(Qt::Alignment alignment)
459{
460 /* Make sure toolbar created: */
461 AssertPtrReturnVoid(m_pToolbar);
462
463 /* Make sure alignment really changed: */
464 if (m_alignment == alignment)
465 return;
466
467 /* Update alignment: */
468 m_alignment = alignment;
469
470 /* Adjust geometry: */
471 adjustGeometry();
472
473 /* Propagate to child to update shape: */
474 m_pToolbar->setAlignment(m_alignment);
475}
476
477void UIMiniToolBar::setAutoHide(bool fAutoHide, bool fPropagateToChild /* = true */)
478{
479 /* Make sure toolbar created: */
480 AssertPtrReturnVoid(m_pToolbar);
481
482 /* Make sure auto-hide really changed: */
483 if (m_fAutoHide == fAutoHide)
484 return;
485
486 /* Update auto-hide: */
487 m_fAutoHide = fAutoHide;
488
489 /* Adjust geometry: */
490 adjustGeometry();
491
492 /* Propagate to child to update action if necessary: */
493 if (fPropagateToChild)
494 m_pToolbar->setAutoHide(m_fAutoHide);
495}
496
497void UIMiniToolBar::setText(const QString &strText)
498{
499 /* Make sure toolbar created: */
500 AssertPtrReturnVoid(m_pToolbar);
501
502 /* Propagate to child: */
503 m_pToolbar->setText(strText);
504}
505
506void UIMiniToolBar::addMenus(const QList<QMenu*> &menus)
507{
508 /* Make sure toolbar created: */
509 AssertPtrReturnVoid(m_pToolbar);
510
511 /* Propagate to child: */
512 m_pToolbar->addMenus(menus);
513}
514
515void UIMiniToolBar::adjustGeometry()
516{
517 /* Resize toolbar to minimum size: */
518 m_pToolbar->resize(m_pToolbar->sizeHint());
519
520 /* Calculate toolbar position: */
521 int iX = 0, iY = 0;
522 iX = width() / 2 - m_pToolbar->width() / 2;
523 switch (m_alignment)
524 {
525 case Qt::AlignTop: iY = 0; break;
526 case Qt::AlignBottom: iY = height() - m_pToolbar->height(); break;
527 default: break;
528 }
529
530 /* Update auto-hide animation: */
531 m_shownToolbarPosition = QPoint(iX, iY);
532 switch (m_alignment)
533 {
534 case Qt::AlignTop: m_hiddenToolbarPosition = m_shownToolbarPosition - QPoint(0, m_pToolbar->height() - 3); break;
535 case Qt::AlignBottom: m_hiddenToolbarPosition = m_shownToolbarPosition + QPoint(0, m_pToolbar->height() - 3); break;
536 }
537 m_pAnimation->update();
538
539 /* Update toolbar geometry if known: */
540 if (property("AnimationState").toString() == "Final")
541 m_pToolbar->move(m_shownToolbarPosition);
542 else
543 m_pToolbar->move(m_hiddenToolbarPosition);
544
545#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
546 /* Adjust window mask: */
547 setMask(m_pToolbar->geometry());
548#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
549}
550
551void UIMiniToolBar::sltHandleToolbarResize()
552{
553 /* Adjust geometry: */
554 adjustGeometry();
555}
556
557void UIMiniToolBar::sltAutoHideToggled()
558{
559 /* Propagate from child: */
560 setAutoHide(m_pToolbar->autoHide(), false);
561}
562
563void UIMiniToolBar::sltHoverEnter()
564{
565 /* Mark as 'hovered' if necessary: */
566 if (!m_fHovered)
567 {
568 m_fHovered = true;
569 emit sigHoverEnter();
570 }
571}
572
573void UIMiniToolBar::sltHoverLeave()
574{
575 /* Mark as 'unhovered' if necessary: */
576 if (m_fHovered)
577 {
578 m_fHovered = false;
579 if (m_fAutoHide)
580 emit sigHoverLeave();
581 }
582}
583
584void UIMiniToolBar::sltHide()
585{
586 LogRel(("GUI: Hide mini-toolbar for window #%d\n", m_iWindowIndex));
587
588#if defined(VBOX_WS_MAC)
589
590 // Nothing
591
592#elif defined(VBOX_WS_WIN)
593
594 /* Reset window state to NONE and hide it: */
595 setWindowState(Qt::WindowNoState);
596 hide();
597
598#elif defined(VBOX_WS_X11)
599
600 /* Just hide window: */
601 hide();
602
603#else
604
605# warning "port me"
606
607#endif
608}
609
610void UIMiniToolBar::sltShow()
611{
612 LogRel(("GUI: Show mini-toolbar for window #%d\n", m_iWindowIndex));
613
614 /* Update transience: */
615 sltAdjustTransience();
616
617#if defined(VBOX_WS_MAC)
618
619 // Nothing
620
621#elif defined(VBOX_WS_WIN)
622
623 // WORKAROUND:
624 // If the host-screen is changed => we should
625 // reset window state to NONE first because
626 // we need an expose on showFullScreen call.
627 if (m_geometryType == GeometryType_Full)
628 setWindowState(Qt::WindowNoState);
629
630 /* Adjust window: */
631 sltAdjust();
632 /* Show window in necessary mode: */
633 switch (m_geometryType)
634 {
635 case GeometryType_Available:
636 {
637 /* Show normal: */
638 show();
639 break;
640 }
641 case GeometryType_Full:
642 {
643 /* Show full-screen: */
644 showFullScreen();
645 break;
646 }
647 }
648
649#elif defined(VBOX_WS_X11)
650
651 /* Show window in necessary mode: */
652 switch (m_geometryType)
653 {
654 case GeometryType_Available:
655 {
656 /* Adjust window: */
657 sltAdjust();
658 /* Show maximized: */
659 if (!isMaximized())
660 showMaximized();
661 break;
662 }
663 case GeometryType_Full:
664 {
665 /* Show full-screen: */
666 showFullScreen();
667 /* Adjust window: */
668 sltAdjust();
669 break;
670 }
671 }
672
673#else
674
675# warning "port me"
676
677#endif
678
679 /* Simulate toolbar auto-hiding: */
680 simulateToolbarAutoHiding();
681}
682
683void UIMiniToolBar::sltAdjust()
684{
685 LogRel(("GUI: Adjust mini-toolbar for window #%d\n", m_iWindowIndex));
686
687 /* Get corresponding host-screen: */
688 const int iHostScreenCount = gpDesktop->screenCount();
689 int iHostScreen = gpDesktop->screenNumber(m_pParent);
690 // WORKAROUND:
691 // When switching host-screen count, especially in complex cases where RDP client is "replacing" host-screen(s) with own virtual-screen(s),
692 // Qt could behave quite arbitrary and laggy, and due to racing there could be a situation when QDesktopWidget::screenNumber() returns -1
693 // as a host-screen number where the parent window is currently located. We should handle this situation anyway, so let's assume the parent
694 // window is located on primary (0) host-screen if it's present or ignore this request at all.
695 if (iHostScreen < 0 || iHostScreen >= iHostScreenCount)
696 {
697 if (iHostScreenCount > 0)
698 {
699 LogRel(("GUI: Mini-toolbar parent window #%d is located on invalid host-screen #%d. Fallback to primary.\n", m_iWindowIndex, iHostScreen));
700 iHostScreen = 0;
701 }
702 else
703 {
704 LogRel(("GUI: Mini-toolbar parent window #%d is located on invalid host-screen #%d. Ignore request.\n", m_iWindowIndex, iHostScreen));
705 return;
706 }
707 }
708
709 /* Get corresponding working area: */
710 QRect workingArea;
711 switch (m_geometryType)
712 {
713 case GeometryType_Available: workingArea = gpDesktop->availableGeometry(iHostScreen); break;
714 case GeometryType_Full: workingArea = gpDesktop->screenGeometry(iHostScreen); break;
715 }
716 Q_UNUSED(workingArea);
717
718#if defined(VBOX_WS_MAC)
719
720 // Nothing
721
722#elif defined(VBOX_WS_WIN)
723
724 switch (m_geometryType)
725 {
726 case GeometryType_Available:
727 {
728 /* Set appropriate window size: */
729 const QSize newSize = workingArea.size();
730 LogRel(("GUI: Resize mini-toolbar for window #%d to %dx%d\n",
731 m_iWindowIndex, newSize.width(), newSize.height()));
732 resize(newSize);
733
734 /* Move window onto required screen: */
735 const QPoint newPosition = workingArea.topLeft();
736 LogRel(("GUI: Move mini-toolbar for window #%d to %dx%d\n",
737 m_iWindowIndex, newPosition.x(), newPosition.y()));
738 move(newPosition);
739
740 break;
741 }
742 case GeometryType_Full:
743 {
744 /* Map window onto required screen: */
745 LogRel(("GUI: Map mini-toolbar for window #%d to screen %d of %d\n",
746 m_iWindowIndex, iHostScreen, qApp->screens().size()));
747 windowHandle()->setScreen(qApp->screens().at(iHostScreen));
748
749 /* Set appropriate window size: */
750 const QSize newSize = workingArea.size();
751 LogRel(("GUI: Resize mini-toolbar for window #%d to %dx%d\n",
752 m_iWindowIndex, newSize.width(), newSize.height()));
753 resize(newSize);
754
755 break;
756 }
757 }
758
759#elif defined(VBOX_WS_X11)
760
761 switch (m_geometryType)
762 {
763 case GeometryType_Available:
764 {
765 /* Make sure we are located on corresponding host-screen: */
766 if ( gpDesktop->screenCount() > 1
767 && (x() != workingArea.x() || y() != workingArea.y()))
768 {
769 // WORKAROUND:
770 // With Qt5 on KDE we can't just move the window onto desired host-screen if
771 // window is maximized. So we have to show it normal first of all:
772 if (isVisible() && isMaximized())
773 showNormal();
774
775 // WORKAROUND:
776 // With Qt5 on X11 we can't just move the window onto desired host-screen if
777 // window size is more than the available geometry (working area) of that
778 // host-screen. So we are resizing it to a smaller size first of all:
779 const QSize newSize = workingArea.size() * .9;
780 LogRel(("GUI: Resize mini-toolbar for window #%d to smaller size %dx%d\n",
781 m_iWindowIndex, newSize.width(), newSize.height()));
782 resize(newSize);
783
784 /* Move window onto required screen: */
785 const QPoint newPosition = workingArea.topLeft();
786 LogRel(("GUI: Move mini-toolbar for window #%d to %dx%d\n",
787 m_iWindowIndex, newPosition.x(), newPosition.y()));
788 move(newPosition);
789 }
790
791 break;
792 }
793 case GeometryType_Full:
794 {
795 /* Determine whether we should use the native full-screen mode: */
796 const bool fUseNativeFullScreen = UICommon::supportsFullScreenMonitorsProtocolX11() &&
797 !gEDataManager->legacyFullscreenModeRequested();
798 if (fUseNativeFullScreen)
799 {
800 /* Tell recent window managers which host-screen this window should be mapped to: */
801 UICommon::setFullScreenMonitorX11(this, iHostScreen);
802 }
803
804 /* Set appropriate window size: */
805 const QSize newSize = workingArea.size();
806 LogRel(("GUI: Resize mini-toolbar for window #%d to %dx%d\n",
807 m_iWindowIndex, newSize.width(), newSize.height()));
808 resize(newSize);
809
810 /* Move window onto required screen: */
811 const QPoint newPosition = workingArea.topLeft();
812 LogRel(("GUI: Move mini-toolbar for window #%d to %dx%d\n",
813 m_iWindowIndex, newPosition.x(), newPosition.y()));
814 move(newPosition);
815
816 /* Re-apply the full-screen state lost on above move(): */
817 setWindowState(Qt::WindowFullScreen);
818
819 break;
820 }
821 }
822
823#else
824
825# warning "port me"
826
827#endif
828}
829
830void UIMiniToolBar::sltAdjustTransience()
831{
832 // WORKAROUND:
833 // Make sure win id is generated,
834 // else Qt5 can crash otherwise.
835 winId();
836 m_pParent->winId();
837
838 /* Add the transience dependency: */
839 windowHandle()->setTransientParent(m_pParent->windowHandle());
840}
841
842void UIMiniToolBar::prepare()
843{
844 /* Install event-filters: */
845 installEventFilter(this);
846 m_pParent->installEventFilter(this);
847
848#if defined(VBOX_WS_WIN)
849 /* No background until first paint-event: */
850 setAttribute(Qt::WA_NoSystemBackground);
851 /* Enable translucency through Qt API: */
852 setAttribute(Qt::WA_TranslucentBackground);
853#elif defined(VBOX_WS_X11)
854 /* Enable translucency through Qt API if supported: */
855 if (uiCommon().isCompositingManagerRunning())
856 setAttribute(Qt::WA_TranslucentBackground);
857#endif /* VBOX_WS_X11 */
858
859 /* Make sure we have no focus: */
860 setFocusPolicy(Qt::NoFocus);
861
862 /* Prepare area: */
863 m_pArea = new QWidget;
864 {
865 /* Allow any area size: */
866 m_pArea->setMinimumSize(QSize(1, 1));
867 /* Configure own background: */
868 QPalette pal = m_pArea->palette();
869 pal.setColor(QPalette::Window, QColor(Qt::transparent));
870 m_pArea->setPalette(pal);
871 /* Layout area according parent-widget: */
872 QVBoxLayout *pMainLayout = new QVBoxLayout(this);
873 pMainLayout->setContentsMargins(0, 0, 0, 0);
874 pMainLayout->addWidget(m_pArea);
875 /* Make sure we have no focus: */
876 m_pArea->setFocusPolicy(Qt::NoFocus);
877 }
878
879 /* Prepare mini-toolbar: */
880 m_pToolbar = new UIMiniToolBarPrivate;
881 {
882 /* Make sure we have no focus: */
883 m_pToolbar->setFocusPolicy(Qt::NoFocus);
884 /* Propagate known options to child: */
885 m_pToolbar->setAutoHide(m_fAutoHide);
886 m_pToolbar->setAlignment(m_alignment);
887 /* Configure own background: */
888 QPalette pal = m_pToolbar->palette();
889 pal.setColor(QPalette::Window, palette().color(QPalette::Window));
890 m_pToolbar->setPalette(pal);
891 /* Configure child connections: */
892 connect(m_pToolbar, &UIMiniToolBarPrivate::sigResized, this, &UIMiniToolBar::sltHandleToolbarResize);
893 connect(m_pToolbar, &UIMiniToolBarPrivate::sigAutoHideToggled, this, &UIMiniToolBar::sltAutoHideToggled);
894 connect(m_pToolbar, &UIMiniToolBarPrivate::sigMinimizeAction, this, &UIMiniToolBar::sigMinimizeAction);
895 connect(m_pToolbar, &UIMiniToolBarPrivate::sigExitAction, this, &UIMiniToolBar::sigExitAction);
896 connect(m_pToolbar, &UIMiniToolBarPrivate::sigCloseAction, this, &UIMiniToolBar::sigCloseAction);
897 /* Add child to area: */
898 m_pToolbar->setParent(m_pArea);
899 /* Make sure we have no focus: */
900 m_pToolbar->setFocusPolicy(Qt::NoFocus);
901 }
902
903 /* Prepare hover-enter/leave timers: */
904 m_pHoverEnterTimer = new QTimer(this);
905 {
906 m_pHoverEnterTimer->setSingleShot(true);
907 m_pHoverEnterTimer->setInterval(500);
908 connect(m_pHoverEnterTimer, &QTimer::timeout, this, &UIMiniToolBar::sltHoverEnter);
909 }
910 m_pHoverLeaveTimer = new QTimer(this);
911 {
912 m_pHoverLeaveTimer->setSingleShot(true);
913 m_pHoverLeaveTimer->setInterval(500);
914 connect(m_pHoverLeaveTimer, &QTimer::timeout, this, &UIMiniToolBar::sltHoverLeave);
915 }
916
917 /* Install 'auto-hide' animation to 'toolbarPosition' property: */
918 m_pAnimation = UIAnimation::installPropertyAnimation(this,
919 "toolbarPosition",
920 "hiddenToolbarPosition", "shownToolbarPosition",
921 SIGNAL(sigHoverEnter()), SIGNAL(sigHoverLeave()),
922 true);
923
924 /* Adjust geometry first time: */
925 adjustGeometry();
926
927#ifdef VBOX_WS_X11
928 /* Hide mini-toolbar from taskbar and pager: */
929 uiCommon().setSkipTaskBarFlag(this);
930 uiCommon().setSkipPagerFlag(this);
931#endif
932}
933
934void UIMiniToolBar::cleanup()
935{
936 /* Stop hover-enter/leave timers: */
937 if (m_pHoverEnterTimer && m_pHoverEnterTimer->isActive())
938 m_pHoverEnterTimer->stop();
939 if (m_pHoverLeaveTimer && m_pHoverLeaveTimer->isActive())
940 m_pHoverLeaveTimer->stop();
941
942 /* Destroy animation before toolbar: */
943 delete m_pAnimation;
944 m_pAnimation = 0;
945
946 /* Destroy toolbar after animation: */
947 delete m_pToolbar;
948 m_pToolbar = 0;
949}
950
951void UIMiniToolBar::enterEvent(QEvent*)
952{
953 /* Stop the hover-leave timer if necessary: */
954 if (m_pHoverLeaveTimer && m_pHoverLeaveTimer->isActive())
955 m_pHoverLeaveTimer->stop();
956
957 /* Start the hover-enter timer: */
958 if (m_pHoverEnterTimer)
959 m_pHoverEnterTimer->start();
960}
961
962void UIMiniToolBar::leaveEvent(QEvent*)
963{
964 // WORKAROUND:
965 // No idea why, but GUI receives mouse leave event
966 // when the mouse cursor is on the border of screen
967 // even if underlying widget is on the border of
968 // screen as well, we should detect and ignore that.
969 // Besides that, this is a good way to keep the
970 // tool-bar visible when the mouse moving through
971 // the desktop strut till the real screen border.
972 const QPoint cursorPosition = QCursor::pos();
973 if ( cursorPosition.y() <= y() + 1
974 || cursorPosition.y() >= y() + height() - 1)
975 return;
976
977 /* Stop the hover-enter timer if necessary: */
978 if (m_pHoverEnterTimer && m_pHoverEnterTimer->isActive())
979 m_pHoverEnterTimer->stop();
980
981 /* Start the hover-leave timer: */
982 if (m_fAutoHide && m_pHoverLeaveTimer)
983 m_pHoverLeaveTimer->start();
984}
985
986void UIMiniToolBar::resizeEvent(QResizeEvent*)
987{
988 /* Adjust geometry: */
989 adjustGeometry();
990}
991
992bool UIMiniToolBar::eventFilter(QObject *pWatched, QEvent *pEvent)
993{
994 /* Detect if we have window activation stolen: */
995 if (pWatched == this && pEvent->type() == QEvent::WindowActivate)
996 {
997#if defined(VBOX_WS_WIN)
998 emit sigNotifyAboutWindowActivationStolen();
999#elif defined(VBOX_WS_X11)
1000 switch (uiCommon().typeOfWindowManager())
1001 {
1002 case X11WMType_GNOMEShell:
1003 case X11WMType_Mutter:
1004 {
1005 // WORKAROUND:
1006 // Under certain WMs we can receive stolen activation event too early,
1007 // returning activation to initial source immediately makes no sense.
1008 // In fact, Qt is not become aware of actual window activation later,
1009 // so we are going to return window activation in let's say 100ms.
1010 QTimer::singleShot(100, this, SLOT(sltNotifyAboutWindowActivationStolen()));
1011 break;
1012 }
1013 default:
1014 {
1015 emit sigNotifyAboutWindowActivationStolen();
1016 break;
1017 }
1018 }
1019#endif /* VBOX_WS_X11 */
1020 }
1021
1022 /* If that's parent window event: */
1023 if (pWatched == m_pParent)
1024 {
1025 switch (pEvent->type())
1026 {
1027 case QEvent::Hide:
1028 {
1029 /* Skip if parent or we are minimized: */
1030 if ( isParentMinimized()
1031 || isMinimized())
1032 break;
1033
1034 /* Asynchronously call for sltHide(): */
1035 LogRel2(("GUI: UIMiniToolBar::eventFilter: Parent hide event\n"));
1036 QMetaObject::invokeMethod(this, "sltHide", Qt::QueuedConnection);
1037 break;
1038 }
1039 case QEvent::Show:
1040 {
1041 /* Skip if parent or we are minimized: */
1042 if ( isParentMinimized()
1043 || isMinimized())
1044 break;
1045
1046 /* Asynchronously call for sltShow(): */
1047 LogRel2(("GUI: UIMiniToolBar::eventFilter: Parent show event\n"));
1048 QMetaObject::invokeMethod(this, "sltShow", Qt::QueuedConnection);
1049 break;
1050 }
1051 case QEvent::Move:
1052 {
1053 // WORKAROUND:
1054 // In certain cases there can be that parent is moving outside of
1055 // full-screen geometry. That for example can happen if virtual
1056 // desktop being changed. We should ignore Move event in such case.
1057 /* Skip if parent is outside of full-screen geometry: */
1058 QMoveEvent *pMoveEvent = static_cast<QMoveEvent*>(pEvent);
1059 if (!gpDesktop->screenGeometry(m_pParent).contains(pMoveEvent->pos()))
1060 break;
1061 /* Skip if parent or we are invisible: */
1062 if ( !m_pParent->isVisible()
1063 || !isVisible())
1064 break;
1065 /* Skip if parent or we are minimized: */
1066 if ( isParentMinimized()
1067 || isMinimized())
1068 break;
1069
1070 /* Asynchronously call for sltShow(): */
1071 LogRel2(("GUI: UIMiniToolBar::eventFilter: Parent move event\n"));
1072 QMetaObject::invokeMethod(this, "sltShow", Qt::QueuedConnection);
1073 break;
1074 }
1075 case QEvent::Resize:
1076 {
1077 /* Skip if parent or we are invisible: */
1078 if ( !m_pParent->isVisible()
1079 || !isVisible())
1080 break;
1081 /* Skip if parent or we are minimized: */
1082 if ( isParentMinimized()
1083 || isMinimized())
1084 break;
1085
1086 /* Asynchronously call for sltShow(): */
1087 LogRel2(("GUI: UIMiniToolBar::eventFilter: Parent resize event\n"));
1088 QMetaObject::invokeMethod(this, "sltShow", Qt::QueuedConnection);
1089 break;
1090 }
1091#ifdef VBOX_WS_X11
1092 case QEvent::WindowStateChange:
1093 {
1094 /* Watch for parent window state changes: */
1095 QWindowStateChangeEvent *pChangeEvent = static_cast<QWindowStateChangeEvent*>(pEvent);
1096 LogRel2(("GUI: UIMiniToolBar::eventFilter: Parent window state changed from %d to %d\n",
1097 (int)pChangeEvent->oldState(), (int)m_pParent->windowState()));
1098
1099 if ( m_pParent->windowState() & Qt::WindowMinimized
1100 && !m_fIsParentMinimized)
1101 {
1102 /* Mark parent window minimized, isMinimized() is not enough due to Qt5vsX11 fight: */
1103 LogRel2(("GUI: UIMiniToolBar::eventFilter: Parent window is minimized\n"));
1104 m_fIsParentMinimized = true;
1105 }
1106 else
1107 if (m_fIsParentMinimized)
1108 {
1109 switch (m_geometryType)
1110 {
1111 case GeometryType_Available:
1112 {
1113 if ( m_pParent->windowState() == Qt::WindowMaximized
1114 && pChangeEvent->oldState() == Qt::WindowNoState)
1115 {
1116 /* Mark parent window non-minimized, isMinimized() is not enough due to Qt5vsX11 fight: */
1117 LogRel2(("GUI: UIMiniToolBar::eventFilter: Parent window is maximized\n"));
1118 m_fIsParentMinimized = false;
1119 }
1120 break;
1121 }
1122 case GeometryType_Full:
1123 {
1124 if ( m_pParent->windowState() == Qt::WindowFullScreen
1125 && pChangeEvent->oldState() == Qt::WindowNoState)
1126 {
1127 /* Mark parent window non-minimized, isMinimized() is not enough due to Qt5vsX11 fight: */
1128 LogRel2(("GUI: UIMiniToolBar::eventFilter: Parent window is full-screen\n"));
1129 m_fIsParentMinimized = false;
1130 }
1131 break;
1132 }
1133 }
1134 }
1135 break;
1136 }
1137#endif /* VBOX_WS_X11 */
1138 default:
1139 break;
1140 }
1141 }
1142
1143 /* Call to base-class: */
1144 return QWidget::eventFilter(pWatched, pEvent);
1145}
1146
1147void UIMiniToolBar::simulateToolbarAutoHiding()
1148{
1149 /* This simulation helps user to notice
1150 * toolbar location, so it will be used only
1151 * 1. if toolbar unhovered and
1152 * 2. auto-hide feature enabled: */
1153 if (m_fHovered || !m_fAutoHide)
1154 return;
1155
1156 /* Simulate hover-leave event: */
1157 m_fHovered = true;
1158 m_pHoverLeaveTimer->start();
1159}
1160
1161void UIMiniToolBar::setToolbarPosition(QPoint point)
1162{
1163 /* Update position: */
1164 AssertPtrReturnVoid(m_pToolbar);
1165 m_pToolbar->move(point);
1166
1167#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
1168 /* Update window mask: */
1169 setMask(m_pToolbar->geometry());
1170#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
1171}
1172
1173QPoint UIMiniToolBar::toolbarPosition() const
1174{
1175 /* Return position: */
1176 AssertPtrReturn(m_pToolbar, QPoint());
1177 return m_pToolbar->pos();
1178}
1179
1180bool UIMiniToolBar::isParentMinimized() const
1181{
1182#ifdef VBOX_WS_X11
1183 return m_fIsParentMinimized;
1184#else
1185 return m_pParent->isMinimized();
1186#endif
1187}
1188
1189#include "UIMiniToolBar.moc"
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use