VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/widgets/UIToolBox.cpp@ 100347

Last change on this file since 100347 was 100075, checked in by vboxsync, 18 months ago

FE/Qt: bugref:10450: Qt6 compatibility bits for QIcon; Good old QIcon::pixmap taking QWindow is deprecated in Qt6; Reworking a lot of calls to use devicePixelRatio acquired beforehand.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.2 KB
Line 
1/* $Id: UIToolBox.cpp 100075 2023-06-05 16:38:02Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIToolBox class implementation.
4 */
5
6/*
7 * Copyright (C) 2006-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 <QCheckBox>
30#include <QLabel>
31#include <QPainter>
32#include <QStyle>
33#include <QVBoxLayout>
34#ifdef VBOX_IS_QT6_OR_LATER
35# include <QWindow>
36#endif
37
38/* GUI includes: */
39#include "QIRichTextLabel.h"
40#include "QIToolButton.h"
41#include "UICommon.h"
42#include "UIIconPool.h"
43#include "UIToolBox.h"
44
45
46/*********************************************************************************************************************************
47* UIToolPageButton definition. *
48*********************************************************************************************************************************/
49/** A QAbstractButton extension used to show collapse/expand icons. More importantly
50 * it is buddy to the title label which may include some mnemonics. This makes it possible
51 * to expand pages via keyboard. */
52class UIToolPageButton : public QAbstractButton
53{
54
55 Q_OBJECT;
56
57public:
58
59 UIToolPageButton(QWidget *pParent = 0);
60 void setPixmap(const QPixmap &pixmap);
61
62protected:
63
64 virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE;
65 virtual QSize sizeHint() const RT_OVERRIDE;
66
67private:
68
69 /** Holds the pixmap of the expand/collapser icon. We keep
70 * QPixmap instead of QIcon since it is rotated when the
71 * the page is expanded and end product of rotation is a pixmap.
72 * and we use QPainter to draw pixmap.*/
73 QPixmap m_pixmap;
74};
75
76
77/*********************************************************************************************************************************
78* UIToolBoxPage definition. *
79*********************************************************************************************************************************/
80
81class UIToolBoxPage : public QIWithRetranslateUI<QWidget>
82{
83
84 Q_OBJECT;
85
86signals:
87
88 void sigShowPageWidget();
89
90public:
91
92 UIToolBoxPage(bool fEnableCheckBoxEnabled = false, QWidget *pParent = 0);
93 void setTitle(const QString &strTitle);
94 void setTitleBackgroundColor(const QColor &color);
95 void setExpanded(bool fExpanded);
96 int index() const;
97 void setIndex(int iIndex);
98 int totalHeight() const;
99 int titleHeight() const;
100 QSize pageWidgetSize() const;
101 void setTitleIcon(const QIcon &icon, const QString &strToolTip);
102
103protected:
104
105 virtual bool eventFilter(QObject *pWatched, QEvent *pEvent) RT_OVERRIDE;
106 virtual void retranslateUi() /* override final */;
107
108private slots:
109
110 void sltHandleEnableToggle(int iState);
111
112private:
113
114 void prepare(bool fEnableCheckBoxEnabled);
115 void setExpandCollapseIcon();
116 /* @p pWidget's ownership is transferred to the page. */
117 void setWidget(QWidget *pWidget);
118
119 bool m_fExpanded;
120 QVBoxLayout *m_pLayout;
121 QWidget *m_pTitleContainerWidget;
122 QLabel *m_pTitleLabel;
123 QLabel *m_pIconLabel;
124 QCheckBox *m_pEnableCheckBox;
125
126 QWidget *m_pWidget;
127 int m_iIndex;
128 bool m_fExpandCollapseIconVisible;
129 QIcon m_expandCollapseIcon;
130 UIToolPageButton *m_pTitleButton;
131 QString m_strTitle;
132 friend class UIToolBox;
133};
134
135
136/*********************************************************************************************************************************
137* UIToolPageButton implementation. *
138*********************************************************************************************************************************/
139
140UIToolPageButton::UIToolPageButton(QWidget *pParent /* = 0 */)
141 : QAbstractButton(pParent)
142{
143}
144
145void UIToolPageButton::paintEvent(QPaintEvent *pEvent)
146{
147 Q_UNUSED(pEvent);
148 if (!m_pixmap.isNull())
149 {
150 QPainter painter(this);
151 painter.drawPixmap(0 /* origin X */,
152 0 /* origin Y */,
153 m_pixmap.width() / m_pixmap.devicePixelRatio() /* width */,
154 m_pixmap.height() / m_pixmap.devicePixelRatio() /* height */,
155 m_pixmap /* pixmap itself */);
156 }
157}
158
159void UIToolPageButton::setPixmap(const QPixmap &pixmap)
160{
161 m_pixmap = pixmap;
162 updateGeometry();
163 update();
164}
165
166QSize UIToolPageButton::sizeHint() const
167{
168 if (m_pixmap.isNull())
169 return QSize(0,0);
170 return m_pixmap.size() / m_pixmap.devicePixelRatio();
171}
172
173
174/*********************************************************************************************************************************
175* UIToolBoxPage implementation. *
176*********************************************************************************************************************************/
177
178UIToolBoxPage::UIToolBoxPage(bool fEnableCheckBoxEnabled /* = false */, QWidget *pParent /* = 0 */)
179 :QIWithRetranslateUI<QWidget>(pParent)
180 , m_fExpanded(false)
181 , m_pLayout(0)
182 , m_pTitleContainerWidget(0)
183 , m_pTitleLabel(0)
184 , m_pIconLabel(0)
185 , m_pEnableCheckBox(0)
186 , m_pWidget(0)
187 , m_iIndex(0)
188 , m_fExpandCollapseIconVisible(true)
189 , m_pTitleButton(0)
190{
191 prepare(fEnableCheckBoxEnabled);
192}
193
194void UIToolBoxPage::setTitle(const QString &strTitle)
195{
196 m_strTitle = strTitle;
197 if (!m_pTitleLabel)
198 return;
199 m_pTitleLabel->setText(strTitle);
200 retranslateUi();
201}
202
203void UIToolBoxPage::prepare(bool fEnableCheckBoxEnabled)
204{
205 m_expandCollapseIcon = UIIconPool::iconSet(":/expanding_collapsing_16px.png");
206
207 m_pLayout = new QVBoxLayout(this);
208 m_pLayout->setContentsMargins(0, 0, 0, 0);
209
210 m_pTitleContainerWidget = new QWidget;
211 m_pTitleContainerWidget->installEventFilter(this);
212 QHBoxLayout *pTitleLayout = new QHBoxLayout(m_pTitleContainerWidget);
213 pTitleLayout->setContentsMargins(qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin),
214 .4f * qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin),
215 qApp->style()->pixelMetric(QStyle::PM_LayoutRightMargin),
216 .4f * qApp->style()->pixelMetric(QStyle::PM_LayoutBottomMargin));
217
218 m_pTitleButton = new UIToolPageButton;
219 pTitleLayout->addWidget(m_pTitleButton);
220 connect(m_pTitleButton, &QAbstractButton::clicked, this, &UIToolBoxPage::sigShowPageWidget);
221
222
223 if (fEnableCheckBoxEnabled)
224 {
225 m_pEnableCheckBox = new QCheckBox;
226 pTitleLayout->addWidget(m_pEnableCheckBox);
227 connect(m_pEnableCheckBox, &QCheckBox::stateChanged, this, &UIToolBoxPage::sltHandleEnableToggle);
228 }
229
230 m_pTitleLabel = new QLabel;
231 m_pTitleLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
232 m_pTitleLabel->setBuddy(m_pTitleButton);
233
234 pTitleLayout->addWidget(m_pTitleLabel);
235 m_pIconLabel = new QLabel;
236 pTitleLayout->addWidget(m_pIconLabel, Qt::AlignLeft);
237 pTitleLayout->addStretch();
238 m_pTitleContainerWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
239 m_pLayout->addWidget(m_pTitleContainerWidget);
240
241 setExpandCollapseIcon();
242 retranslateUi();
243}
244
245void UIToolBoxPage::setWidget(QWidget *pWidget)
246{
247 if (!m_pLayout || !pWidget)
248 return;
249 m_pWidget = pWidget;
250 m_pLayout->addWidget(m_pWidget);
251
252 if (m_pEnableCheckBox)
253 m_pWidget->setEnabled(m_pEnableCheckBox->checkState() == Qt::Checked);
254
255 m_pWidget->hide();
256}
257
258void UIToolBoxPage::setTitleBackgroundColor(const QColor &color)
259{
260 if (!m_pTitleLabel)
261 return;
262 QPalette palette = m_pTitleContainerWidget->palette();
263 palette.setColor(QPalette::Window, color);
264 m_pTitleContainerWidget->setPalette(palette);
265 m_pTitleContainerWidget->setAutoFillBackground(true);
266}
267
268void UIToolBoxPage::setExpanded(bool fVisible)
269{
270 if (m_pWidget)
271 m_pWidget->setVisible(fVisible);
272 m_fExpanded = fVisible;
273 setExpandCollapseIcon();
274}
275
276int UIToolBoxPage::index() const
277{
278 return m_iIndex;
279}
280
281void UIToolBoxPage::setIndex(int iIndex)
282{
283 m_iIndex = iIndex;
284}
285
286int UIToolBoxPage::totalHeight() const
287{
288 return pageWidgetSize().height() + titleHeight();
289}
290
291void UIToolBoxPage::setTitleIcon(const QIcon &icon, const QString &strToolTip)
292{
293 if (!m_pIconLabel)
294 return;
295 if (icon.isNull())
296 {
297 m_pIconLabel->setPixmap(QPixmap());
298 return;
299 }
300 const int iMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
301#ifndef VBOX_IS_QT6_OR_LATER /* QIcon::pixmap taking QWindow is deprecated in Qt6 */
302 m_pIconLabel->setPixmap(icon.pixmap(windowHandle(), QSize(iMetric, iMetric)));
303#else
304 const qreal fDevicePixelRatio = window() && window()->windowHandle() ? window()->windowHandle()->devicePixelRatio() : 1;
305 m_pIconLabel->setPixmap(icon.pixmap(QSize(iMetric, iMetric), fDevicePixelRatio));
306#endif
307 m_pIconLabel->setToolTip(strToolTip);
308}
309
310int UIToolBoxPage::titleHeight() const
311{
312 if (m_pTitleContainerWidget && m_pTitleContainerWidget->sizeHint().isValid())
313 return m_pTitleContainerWidget->sizeHint().height();
314 return 0;
315}
316
317QSize UIToolBoxPage::pageWidgetSize() const
318{
319 if (m_pWidget && m_pWidget->sizeHint().isValid())
320 return m_pWidget->sizeHint();
321 return QSize();
322}
323
324bool UIToolBoxPage::eventFilter(QObject *pWatched, QEvent *pEvent)
325{
326 if (pWatched == m_pTitleContainerWidget)
327 {
328 if (pEvent->type() == QEvent::MouseButtonPress)
329 emit sigShowPageWidget();
330 }
331 return QWidget::eventFilter(pWatched, pEvent);
332
333}
334
335void UIToolBoxPage::sltHandleEnableToggle(int iState)
336{
337 if (m_pWidget)
338 m_pWidget->setEnabled(iState == Qt::Checked);
339}
340
341void UIToolBoxPage::setExpandCollapseIcon()
342{
343 if (!m_fExpandCollapseIconVisible)
344 {
345 m_pTitleButton->setVisible(false);
346 return;
347 }
348 const int iMetric = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize);
349#ifndef VBOX_IS_QT6_OR_LATER /* QIcon::pixmap taking QWindow is deprecated in Qt6 */
350 QPixmap basePixmap = m_expandCollapseIcon.pixmap(windowHandle(), QSize(iMetric, iMetric));
351#else
352 const qreal fDevicePixelRatio = window() && window()->windowHandle() ? window()->windowHandle()->devicePixelRatio() : 1;
353 QPixmap basePixmap = m_expandCollapseIcon.pixmap(QSize(iMetric, iMetric), fDevicePixelRatio);
354#endif
355 if (!m_fExpanded)
356 m_pTitleButton->setPixmap(basePixmap);
357 else
358 {
359 QTransform transform;
360 transform.rotate(90);
361 QPixmap transformedPixmap = basePixmap.transformed(transform);
362 transformedPixmap.setDevicePixelRatio(basePixmap.devicePixelRatio());
363 m_pTitleButton->setPixmap(transformedPixmap);
364 }
365}
366
367void UIToolBoxPage::retranslateUi()
368{
369 if (m_pTitleButton)
370 m_pTitleButton->setToolTip(UIToolBox::tr("Expands the page \"%1\"").arg(m_strTitle.remove('&')));
371}
372
373
374/*********************************************************************************************************************************
375* UIToolBox implementation. *
376*********************************************************************************************************************************/
377
378UIToolBox::UIToolBox(QWidget *pParent /* = 0 */)
379 : QIWithRetranslateUI<QFrame>(pParent)
380 , m_iCurrentPageIndex(-1)
381 , m_iPageCount(0)
382{
383 prepare();
384}
385
386bool UIToolBox::insertPage(int iIndex, QWidget *pWidget, const QString &strTitle, bool fAddEnableCheckBox /* = false */)
387{
388 if (m_pages.contains(iIndex))
389 return false;
390
391 /* Remove the stretch from the end of the layout: */
392 QLayoutItem *pItem = m_pMainLayout->takeAt(m_pMainLayout->count() - 1);
393 delete pItem;
394
395 ++m_iPageCount;
396 UIToolBoxPage *pNewPage = new UIToolBoxPage(fAddEnableCheckBox, 0);;
397
398 pNewPage->setWidget(pWidget);
399 pNewPage->setIndex(iIndex);
400 pNewPage->setTitle(strTitle);
401
402 const QPalette pal = QApplication::palette();
403 QColor tabBackgroundColor = pal.color(QPalette::Active, QPalette::Highlight).lighter(130);
404 pNewPage->setTitleBackgroundColor(tabBackgroundColor);
405
406 m_pages[iIndex] = pNewPage;
407 m_pMainLayout->insertWidget(iIndex, pNewPage);
408
409 connect(pNewPage, &UIToolBoxPage::sigShowPageWidget,
410 this, &UIToolBox::sltHandleShowPageWidget);
411
412 /* Add stretch at the end: */
413 m_pMainLayout->addStretch(1);
414 return iIndex;
415}
416
417QSize UIToolBox::minimumSizeHint() const
418{
419
420 int iMaxPageHeight = 0;
421 int iTotalTitleHeight = 0;
422 int iWidth = 0;
423 foreach(UIToolBoxPage *pPage, m_pages)
424 {
425 QSize pageWidgetSize(pPage->pageWidgetSize());
426 iMaxPageHeight = qMax(iMaxPageHeight, pageWidgetSize.height());
427 iTotalTitleHeight += pPage->titleHeight();
428 iWidth = qMax(pageWidgetSize.width(), iWidth);
429 }
430 int iHeight = m_iPageCount * (qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin) +
431 qApp->style()->pixelMetric(QStyle::PM_LayoutBottomMargin)) +
432 iTotalTitleHeight +
433 iMaxPageHeight;
434 return QSize(iWidth, iHeight);
435}
436
437void UIToolBox::setPageEnabled(int iIndex, bool fEnabled)
438{
439 m_pages.value(iIndex)->setEnabled(fEnabled);
440}
441
442void UIToolBox::setPageTitle(int iIndex, const QString &strTitle)
443{
444 QMap<int, UIToolBoxPage*>::iterator iterator = m_pages.find(iIndex);
445 if (iterator == m_pages.end())
446 return;
447 iterator.value()->setTitle(strTitle);
448}
449
450void UIToolBox::setPageTitleIcon(int iIndex, const QIcon &icon, const QString &strIconToolTip /* = QString() */)
451{
452 QMap<int, UIToolBoxPage*>::iterator iterator = m_pages.find(iIndex);
453 if (iterator == m_pages.end())
454 return;
455 iterator.value()->setTitleIcon(icon, strIconToolTip);
456}
457
458void UIToolBox::setCurrentPage(int iIndex)
459{
460 m_iCurrentPageIndex = iIndex;
461 QMap<int, UIToolBoxPage*>::iterator iterator = m_pages.find(iIndex);
462 if (iterator == m_pages.end())
463 return;
464 foreach(UIToolBoxPage *pPage, m_pages)
465 pPage->setExpanded(false);
466
467 iterator.value()->setExpanded(true);
468}
469
470void UIToolBox::retranslateUi()
471{
472}
473
474void UIToolBox::prepare()
475{
476 m_pMainLayout = new QVBoxLayout(this);
477 m_pMainLayout->addStretch();
478
479 retranslateUi();
480}
481
482void UIToolBox::sltHandleShowPageWidget()
483{
484 UIToolBoxPage *pPage = qobject_cast<UIToolBoxPage*>(sender());
485 if (!pPage)
486 return;
487 setCurrentPage(pPage->index());
488 update();
489}
490
491#include "UIToolBox.moc"
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette