VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/widgets/UIPopupBox.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: 13.1 KB
Line 
1/* $Id: UIPopupBox.cpp 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIPopupBox/UIPopupBoxGroup classes implementation.
4 */
5
6/*
7 * Copyright (C) 2010-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 <QPainter>
26# include <QPaintEvent>
27# include <QStyle>
28# include <QVBoxLayout>
29
30/* GUI includes: */
31# include "UIPopupBox.h"
32# ifdef VBOX_WS_MAC
33# include "UIImageTools.h"
34# endif /* VBOX_WS_MAC */
35
36#endif /* !VBOX_WITH_PRECOMPILED_HEADERS */
37
38
39/*********************************************************************************************************************************
40* Class UIPopupBox implementation. *
41*********************************************************************************************************************************/
42
43UIPopupBox::UIPopupBox(QWidget *pParent)
44 : QWidget(pParent)
45 , m_pTitleIcon(0)
46 , m_pWarningIcon(0)
47 , m_pTitleLabel(0)
48 , m_fLinkEnabled(false)
49 , m_fOpened(true)
50 , m_fHovered(false)
51 , m_pContentWidget(0)
52 , m_pLabelPath(0)
53 , m_iArrowWidth(9)
54{
55 /* Configure self: */
56 installEventFilter(this);
57
58 /* Configure painter-path: */
59 m_arrowPath.lineTo(m_iArrowWidth / 2.0, m_iArrowWidth / 2.0);
60 m_arrowPath.lineTo(m_iArrowWidth, 0);
61
62 /* Create main-layout: */
63 QVBoxLayout *pMainLayout = new QVBoxLayout(this);
64 if (pMainLayout)
65 {
66 /* Create title-layout: */
67 QHBoxLayout *pTitleLayout = new QHBoxLayout;
68 if (pTitleLayout)
69 {
70 /* Create title-icon label. */
71 m_pTitleIcon = new QLabel;
72 if (m_pTitleIcon)
73 {
74 /* Configure label: */
75 m_pTitleIcon->installEventFilter(this);
76
77 /* Add into layout: */
78 pTitleLayout->addWidget(m_pTitleIcon);
79 }
80 /* Create warning-icon label. */
81 m_pWarningIcon = new QLabel;
82 if (m_pWarningIcon)
83 {
84 /* Configure label: */
85 m_pWarningIcon->setHidden(true);
86 m_pWarningIcon->installEventFilter(this);
87
88 /* Add into layout: */
89 pTitleLayout->addWidget(m_pWarningIcon);
90 }
91 /* Create title-text label. */
92 m_pTitleLabel = new QLabel;
93 if (m_pTitleLabel)
94 {
95 /* Configure label: */
96 m_pTitleLabel->installEventFilter(this);
97 connect(m_pTitleLabel, SIGNAL(linkActivated(const QString)),
98 this, SIGNAL(sigTitleClicked(const QString)));
99
100 /* Add into layout: */
101 pTitleLayout->addWidget(m_pTitleLabel, Qt::AlignLeft);
102 }
103
104 /* Add into layout: */
105 pMainLayout->addLayout(pTitleLayout);
106 }
107 }
108
109}
110
111UIPopupBox::~UIPopupBox()
112{
113 /* Delete label painter-path if any: */
114 if (m_pLabelPath)
115 delete m_pLabelPath;
116}
117
118void UIPopupBox::setTitleIcon(const QIcon &icon)
119{
120 /* Remember new title-icon: */
121 m_titleIcon = icon;
122 /* Update title-icon: */
123 updateTitleIcon();
124 /* Recalculate title-size: */
125 recalc();
126}
127
128QIcon UIPopupBox::titleIcon() const
129{
130 return m_titleIcon;
131}
132
133void UIPopupBox::setWarningIcon(const QIcon &icon)
134{
135 /* Remember new warning-icon: */
136 m_warningIcon = icon;
137 /* Update warning-icon: */
138 updateWarningIcon();
139 /* Recalculate title-size: */
140 recalc();
141}
142
143QIcon UIPopupBox::warningIcon() const
144{
145 return m_warningIcon;
146}
147
148void UIPopupBox::setTitle(const QString &strTitle)
149{
150 /* Remember new title: */
151 m_strTitle = strTitle;
152 /* Update title: */
153 updateTitle();
154 /* Recalculate title-size: */
155 recalc();
156}
157
158QString UIPopupBox::title() const
159{
160 return m_strTitle;
161}
162
163void UIPopupBox::setTitleLink(const QString &strLink)
164{
165 /* Remember new title-link: */
166 m_strLink = strLink;
167 /* Update title: */
168 updateTitle();
169}
170
171QString UIPopupBox::titleLink() const
172{
173 return m_strLink;
174}
175
176void UIPopupBox::setTitleLinkEnabled(bool fEnabled)
177{
178 /* Remember new title-link availability flag: */
179 m_fLinkEnabled = fEnabled;
180 /* Update title: */
181 updateTitle();
182}
183
184bool UIPopupBox::isTitleLinkEnabled() const
185{
186 return m_fLinkEnabled;
187}
188
189void UIPopupBox::setContentWidget(QWidget *pWidget)
190{
191 /* Cleanup old content-widget if any: */
192 if (m_pContentWidget)
193 {
194 m_pContentWidget->removeEventFilter(this);
195 layout()->removeWidget(m_pContentWidget);
196 }
197 /* Prepare new content-wodget: */
198 m_pContentWidget = pWidget;
199 layout()->addWidget(m_pContentWidget);
200 m_pContentWidget->installEventFilter(this);
201 recalc();
202}
203
204QWidget* UIPopupBox::contentWidget() const
205{
206 return m_pContentWidget;
207}
208
209void UIPopupBox::setOpen(bool fOpened)
210{
211 /* Check if we should toggle popup-box: */
212 if (m_fOpened == fOpened)
213 return;
214
215 /* Store new value: */
216 m_fOpened = fOpened;
217
218 /* Update content-widget if present or this itself: */
219 if (m_pContentWidget)
220 m_pContentWidget->setVisible(m_fOpened);
221 else
222 update();
223
224 /* Notify listeners about content-widget visibility: */
225 if (m_pContentWidget && m_pContentWidget->isVisible())
226 emit sigUpdateContentWidget();
227}
228
229void UIPopupBox::toggleOpen()
230{
231 /* Switch 'opened' state: */
232 setOpen(!m_fOpened);
233
234 /* Notify listeners about toggling: */
235 emit sigToggled(m_fOpened);
236}
237
238bool UIPopupBox::isOpen() const
239{
240 return m_fOpened;
241}
242
243bool UIPopupBox::event(QEvent *pEvent)
244{
245 /* Handle know event types: */
246 switch (pEvent->type())
247 {
248 case QEvent::Show:
249 case QEvent::ScreenChangeInternal:
250 {
251 /* Update pixmaps: */
252 updateTitleIcon();
253 updateWarningIcon();
254 break;
255 }
256 default:
257 break;
258 }
259
260 /* Call to base-class: */
261 return QWidget::event(pEvent);
262}
263
264bool UIPopupBox::eventFilter(QObject *pObject, QEvent *pEvent)
265{
266 /* Handle all mouse-event to update hover: */
267 QEvent::Type type = pEvent->type();
268 if (type == QEvent::Enter ||
269 type == QEvent::Leave ||
270 type == QEvent::MouseMove ||
271 type == QEvent::Wheel)
272 updateHover();
273 /* Call to base-class: */
274 return QWidget::eventFilter(pObject, pEvent);
275}
276
277void UIPopupBox::resizeEvent(QResizeEvent *pEvent)
278{
279 /* Recalculate title-size: */
280 recalc();
281 /* Call to base-class: */
282 QWidget::resizeEvent(pEvent);
283}
284
285void UIPopupBox::paintEvent(QPaintEvent *pEvent)
286{
287 /* Create painter: */
288 QPainter painter(this);
289 painter.setClipRect(pEvent->rect());
290
291 QPalette pal = palette();
292 painter.setClipPath(*m_pLabelPath);
293 QColor base = pal.color(QPalette::Active, QPalette::Window);
294 QRect rect = QRect(QPoint(0, 0), size()).adjusted(0, 0, -1, -1);
295 /* Base background */
296 painter.fillRect(QRect(QPoint(0, 0), size()), pal.brush(QPalette::Active, QPalette::Base));
297 /* Top header background */
298 const int iMaxHeightHint = qMax(m_pTitleLabel->sizeHint().height(),
299 m_pTitleIcon->sizeHint().height());
300 QLinearGradient lg(rect.x(), rect.y(), rect.x(), rect.y() + 2 * 5 + iMaxHeightHint);
301 lg.setColorAt(0, base.darker(95));
302 lg.setColorAt(1, base.darker(110));
303 int theight = rect.height();
304 if (m_fOpened)
305 theight = 2 * 5 + iMaxHeightHint;
306 painter.fillRect(QRect(rect.x(), rect.y(), rect.width(), theight), lg);
307 /* Outer round rectangle line */
308 painter.setClipping(false);
309 painter.strokePath(*m_pLabelPath, base.darker(110));
310 /* Arrow */
311 if (m_fHovered)
312 {
313 painter.setBrush(base.darker(106));
314 painter.setPen(QPen(base.darker(128), 3, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
315 QSizeF s = m_arrowPath.boundingRect().size();
316 if (m_fOpened)
317 {
318 painter.translate(rect.x() + rect.width() - s.width() - 10, rect.y() + theight / 2 + s.height() / 2);
319 /* Flip */
320 painter.scale(1, -1);
321 }
322 else
323 painter.translate(rect.x() + rect.width() - s.width() - 10, rect.y() + theight / 2 - s.height() / 2 + 1);
324
325 painter.setRenderHint(QPainter::Antialiasing);
326 painter.drawPath(m_arrowPath);
327 }
328}
329
330void UIPopupBox::mouseDoubleClickEvent(QMouseEvent *)
331{
332 /* Toggle popup-box: */
333 toggleOpen();
334}
335
336void UIPopupBox::updateTitleIcon()
337{
338 /* Assign title-icon: */
339 const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
340 m_pTitleIcon->setPixmap(m_titleIcon.pixmap(window()->windowHandle(), QSize(iIconMetric, iIconMetric)));
341}
342
343void UIPopupBox::updateWarningIcon()
344{
345 /* Hide warning-icon if its null: */
346 m_pWarningIcon->setHidden(m_warningIcon.isNull());
347
348 /* Assign warning-icon: */
349 const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
350 m_pWarningIcon->setPixmap(m_warningIcon.pixmap(window()->windowHandle(), QSize(iIconMetric, iIconMetric)));
351}
352
353void UIPopupBox::updateTitle()
354{
355 /* If title-link is disabled or not set: */
356 if (!m_fLinkEnabled || m_strLink.isEmpty())
357 {
358 /* We should just set simple text title: */
359 m_pTitleLabel->setText(QString("<b>%1</b>").arg(m_strTitle));
360 }
361 /* If title-link is enabled and set: */
362 else if (m_fLinkEnabled && !m_strLink.isEmpty())
363 {
364 /* We should set html reference title: */
365 QPalette pal = m_pTitleLabel->palette();
366 m_pTitleLabel->setText(QString("<b><a style=\"text-decoration: none; color: %1\" href=\"%2\">%3</a></b>")
367 .arg(m_fHovered ? pal.color(QPalette::Link).name() : pal.color(QPalette::WindowText).name())
368 .arg(m_strLink)
369 .arg(m_strTitle));
370 }
371}
372
373void UIPopupBox::updateHover()
374{
375 /* Calculate new header-hover state: */
376 bool fNewHovered = m_fHovered;
377 if (m_pLabelPath && m_pLabelPath->contains(mapFromGlobal(QCursor::pos())))
378 fNewHovered = true;
379 else
380 fNewHovered = false;
381
382 /* Check if we should toggle hover: */
383 if (m_fHovered == fNewHovered)
384 return;
385
386 /* If header-hover state switched from disabled to enabled: */
387 if (!m_fHovered && fNewHovered)
388 /* Notify listeners: */
389 emit sigGotHover();
390
391 /* Toggle hover: */
392 toggleHover(fNewHovered);
393}
394
395void UIPopupBox::revokeHover()
396{
397 /* Check if we should toggle hover: */
398 if (m_fHovered == false)
399 return;
400
401 /* Toggle hover off: */
402 toggleHover(false);
403}
404
405void UIPopupBox::toggleHover(bool fHeaderHover)
406{
407 /* Remember header-hover state: */
408 m_fHovered = fHeaderHover;
409
410 /* Update title: */
411 updateTitle();
412
413 /* Call for update: */
414 update();
415}
416
417void UIPopupBox::recalc()
418{
419 if (m_pLabelPath)
420 delete m_pLabelPath;
421 QRect rect = QRect(QPoint(0, 0), size()).adjusted(0, 0, -1, -1);
422 int d = 18; // 22
423 m_pLabelPath = new QPainterPath(QPointF(rect.x() + rect.width() - d, rect.y()));
424 m_pLabelPath->arcTo(QRectF(rect.x(), rect.y(), d, d), 90, 90);
425 m_pLabelPath->arcTo(QRectF(rect.x(), rect.y() + rect.height() - d, d, d), 180, 90);
426 m_pLabelPath->arcTo(QRectF(rect.x() + rect.width() - d, rect.y() + rect.height() - d, d, d), 270, 90);
427 m_pLabelPath->arcTo(QRectF(rect.x() + rect.width() - d, rect.y(), d, d), 0, 90);
428 m_pLabelPath->closeSubpath();
429 update();
430}
431
432
433/*********************************************************************************************************************************
434* Class UIPopupBoxGroup implementation. *
435*********************************************************************************************************************************/
436
437UIPopupBoxGroup::UIPopupBoxGroup(QObject *pParent)
438 : QObject(pParent)
439{
440}
441
442UIPopupBoxGroup::~UIPopupBoxGroup()
443{
444 /* Clear the list early: */
445 m_list.clear();
446}
447
448void UIPopupBoxGroup::addPopupBox(UIPopupBox *pPopupBox)
449{
450 /* Add popup-box into list: */
451 m_list << pPopupBox;
452
453 /* Connect got-hover signal of the popup-box to hover-change slot of the popup-box group: */
454 connect(pPopupBox, SIGNAL(sigGotHover()), this, SLOT(sltHoverChanged()));
455}
456
457void UIPopupBoxGroup::sltHoverChanged()
458{
459 /* Fetch the sender: */
460 UIPopupBox *pPopupBox = qobject_cast<UIPopupBox*>(sender());
461
462 /* Check if sender popup-box exists/registered: */
463 if (!pPopupBox || !m_list.contains(pPopupBox))
464 return;
465
466 /* Filter the sender: */
467 QList<UIPopupBox*> list(m_list);
468 list.removeOne(pPopupBox);
469
470 /* Notify all other popup-boxes: */
471 for (int i = 0; i < list.size(); ++i)
472 list[i]->revokeHover();
473}
474
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use