VirtualBox

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

Last change on this file was 103988, checked in by vboxsync, 2 months ago

FE/Qt. bugref:10624. Adding missing override keywords gcc has found.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use