VirtualBox

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

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

FE/Qt: bugref:8938. Changing connection syntax in UILineTextEdit and UIPopupPane related classes.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use