VirtualBox

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

Last change on this file was 104358, checked in by vboxsync, 5 weeks ago

FE/Qt. bugref:10622. More refactoring around the retranslation functionality.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use