VirtualBox

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

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

scm --update-copyright-year

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

© 2023 Oracle
ContactPrivacy policyTerms of Use