VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/wizards/UINativeWizard.cpp@ 98103

Last change on this file since 98103 was 98103, checked in by vboxsync, 22 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.7 KB
Line 
1/* $Id: UINativeWizard.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UINativeWizard 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 <QHBoxLayout>
30#include <QLabel>
31#include <QPainter>
32#include <QPushButton>
33#include <QStackedWidget>
34#include <QStyle>
35#include <QVBoxLayout>
36#include <QWindow>
37
38/* GUI includes: */
39#include "QIRichTextLabel.h"
40#include "UICommon.h"
41#include "UIDesktopWidgetWatchdog.h"
42#include "UIExtraDataManager.h"
43#include "UIIconPool.h"
44#include "UIMessageCenter.h"
45#include "UINativeWizard.h"
46#include "UINativeWizardPage.h"
47#include "UINotificationCenter.h"
48
49
50#ifdef VBOX_WS_MAC
51UIFrame::UIFrame(QWidget *pParent)
52 : QWidget(pParent)
53{
54}
55
56void UIFrame::paintEvent(QPaintEvent *pEvent)
57{
58 /* Sanity check: */
59 AssertPtrReturnVoid(pEvent);
60
61 /* Prepare painter: */
62 QPainter painter(this);
63
64 /* Limit painting with incoming rectangle: */
65 painter.setClipRect(pEvent->rect());
66
67 /* Check whether we should use Active or Inactive palette: */
68 const bool fActive = parentWidget() && parentWidget()->isActiveWindow();
69
70 /* Paint background: */
71 QColor backgroundColor = QGuiApplication::palette().color(fActive ? QPalette::Active : QPalette::Inactive, QPalette::Window);
72 backgroundColor.setAlpha(100);
73 painter.setPen(backgroundColor);
74 painter.setBrush(backgroundColor);
75 painter.drawRect(rect());
76
77 /* Paint borders: */
78 painter.setPen(QGuiApplication::palette().color(fActive ? QPalette::Active : QPalette::Inactive, QPalette::Window).darker(130));
79 QLine line1(0, 0, rect().width() - 1, 0);
80 QLine line2(rect().width() - 1, 0, rect().width() - 1, rect().height() - 1);
81 QLine line3(rect().width() - 1, rect().height() - 1, 0, rect().height() - 1);
82 QLine line4(0, rect().height() - 1, 0, 0);
83 painter.drawLine(line1);
84 painter.drawLine(line2);
85 painter.drawLine(line3);
86 painter.drawLine(line4);
87}
88#endif /* VBOX_WS_MAC */
89
90
91UINativeWizard::UINativeWizard(QWidget *pParent,
92 WizardType enmType,
93 WizardMode enmMode /* = WizardMode_Auto */,
94 const QString &strHelpTag /* = QString() */)
95 : QIWithRetranslateUI<QDialog>(pParent)
96 , m_enmType(enmType)
97 , m_enmMode(enmMode == WizardMode_Auto ? gEDataManager->modeForWizardType(m_enmType) : enmMode)
98 , m_strHelpHashtag(strHelpTag)
99 , m_iLastIndex(-1)
100 , m_pLabelPixmap(0)
101 , m_pLayoutRight(0)
102 , m_pLabelPageTitle(0)
103 , m_pWidgetStack(0)
104 , m_pNotificationCenter(0)
105{
106 prepare();
107}
108
109UINativeWizard::~UINativeWizard()
110{
111 cleanup();
112}
113
114UINotificationCenter *UINativeWizard::notificationCenter() const
115{
116 return m_pNotificationCenter;
117}
118
119bool UINativeWizard::handleNotificationProgressNow(UINotificationProgress *pProgress)
120{
121 wizardButton(WizardButtonType_Expert)->setEnabled(false);
122 const bool fResult = m_pNotificationCenter->handleNow(pProgress);
123 wizardButton(WizardButtonType_Expert)->setEnabled(true);
124 return fResult;
125}
126
127QPushButton *UINativeWizard::wizardButton(const WizardButtonType &enmType) const
128{
129 return m_buttons.value(enmType);
130}
131
132int UINativeWizard::exec()
133{
134 /* Init wizard: */
135 init();
136
137 /* Call to base-class: */
138 return QIWithRetranslateUI<QDialog>::exec();
139}
140
141void UINativeWizard::setPixmapName(const QString &strName)
142{
143 m_strPixmapName = strName;
144}
145
146bool UINativeWizard::isPageVisible(int iIndex) const
147{
148 return !m_invisiblePages.contains(iIndex);
149}
150
151void UINativeWizard::setPageVisible(int iIndex, bool fVisible)
152{
153 AssertMsgReturnVoid(iIndex || fVisible, ("Can't hide 1st wizard page!\n"));
154 if (fVisible)
155 m_invisiblePages.remove(iIndex);
156 else
157 m_invisiblePages.insert(iIndex);
158 /* Update the button labels since the last visible page might have changed. Thus 'Next' <-> 'Finish' might be needed: */
159 retranslateUi();
160}
161
162int UINativeWizard::addPage(UINativeWizardPage *pPage)
163{
164 /* Sanity check: */
165 AssertPtrReturn(pPage, -1);
166 AssertPtrReturn(pPage->layout(), -1);
167
168 /* Adjust page layout: */
169 const int iL = 0;
170 const int iT = 0;
171 const int iR = qApp->style()->pixelMetric(QStyle::PM_LayoutRightMargin);
172 const int iB = qApp->style()->pixelMetric(QStyle::PM_LayoutBottomMargin);
173 pPage->layout()->setContentsMargins(iL, iT, iR, iB);
174
175 /* Add page to wizard's stack: */
176 m_pWidgetStack->blockSignals(true);
177 const int iIndex = m_pWidgetStack->addWidget(pPage);
178 m_pWidgetStack->blockSignals(false);
179
180 /* Make sure wizard is aware of page validity changes: */
181 connect(pPage, &UINativeWizardPage::completeChanged,
182 this, &UINativeWizard::sltCompleteChanged);
183
184 /* Returns added page index: */
185 return iIndex;
186}
187
188void UINativeWizard::retranslateUi()
189{
190 /* Translate Help button: */
191 QPushButton *pButtonHelp = wizardButton(WizardButtonType_Help);
192 if (pButtonHelp)
193 {
194 pButtonHelp->setText(tr("&Help"));
195 pButtonHelp->setToolTip(tr("Open corresponding Help topic."));
196 }
197
198 /* Translate basic/expert button: */
199 QPushButton *pButtonExpert = wizardButton(WizardButtonType_Expert);
200 AssertMsgReturnVoid(pButtonExpert, ("No Expert wizard button found!\n"));
201 switch (m_enmMode)
202 {
203 case WizardMode_Basic:
204 pButtonExpert->setText(tr("&Expert Mode"));
205 pButtonExpert->setToolTip(tr("Switch to the Expert Mode, "
206 "a one-page dialog for experienced users."));
207 break;
208 case WizardMode_Expert:
209 pButtonExpert->setText(tr("&Guided Mode"));
210 pButtonExpert->setToolTip(tr("Switch to the Guided Mode, "
211 "a step-by-step dialog with detailed explanations."));
212 break;
213 default:
214 AssertMsgFailed(("Invalid wizard mode: %d", m_enmMode));
215 break;
216 }
217
218 /* Translate Back button: */
219 QPushButton *pButtonBack = wizardButton(WizardButtonType_Back);
220 AssertMsgReturnVoid(pButtonBack, ("No Back wizard button found!\n"));
221 pButtonBack->setText(tr("&Back"));
222 pButtonBack->setToolTip(tr("Go to previous wizard page."));
223
224 /* Translate Next button: */
225 QPushButton *pButtonNext = wizardButton(WizardButtonType_Next);
226 AssertMsgReturnVoid(pButtonNext, ("No Next wizard button found!\n"));
227 if (!isLastVisiblePage(m_pWidgetStack->currentIndex()))
228 {
229 pButtonNext->setText(tr("&Next"));
230 pButtonNext->setToolTip(tr("Go to next wizard page."));
231 }
232 else
233 {
234 pButtonNext->setText(tr("&Finish"));
235 pButtonNext->setToolTip(tr("Commit all wizard data."));
236 }
237
238 /* Translate Cancel button: */
239 QPushButton *pButtonCancel = wizardButton(WizardButtonType_Cancel);
240 AssertMsgReturnVoid(pButtonCancel, ("No Cancel wizard button found!\n"));
241 pButtonCancel->setText(tr("&Cancel"));
242 pButtonCancel->setToolTip(tr("Cancel wizard execution."));
243}
244
245void UINativeWizard::sltCurrentIndexChanged(int iIndex /* = -1 */)
246{
247 /* Update translation: */
248 retranslateUi();
249
250 /* Sanity check: */
251 AssertPtrReturnVoid(m_pWidgetStack);
252
253 /* -1 means current one page: */
254 if (iIndex == -1)
255 iIndex = m_pWidgetStack->currentIndex();
256
257 /* Hide/show Expert button (hidden by default): */
258 bool fIsExpertButtonAvailable = false;
259 /* Show Expert button for 1st page: */
260 if (iIndex == 0)
261 fIsExpertButtonAvailable = true;
262 /* Hide/show Expert button finally: */
263 QPushButton *pButtonExpert = wizardButton(WizardButtonType_Expert);
264 AssertMsgReturnVoid(pButtonExpert, ("No Expert wizard button found!\n"));
265 pButtonExpert->setVisible(fIsExpertButtonAvailable);
266
267 /* Disable/enable Back button: */
268 QPushButton *pButtonBack = wizardButton(WizardButtonType_Back);
269 AssertMsgReturnVoid(pButtonBack, ("No Back wizard button found!\n"));
270 pButtonBack->setEnabled(iIndex > 0);
271
272 /* Initialize corresponding page: */
273 UINativeWizardPage *pPage = qobject_cast<UINativeWizardPage*>(m_pWidgetStack->widget(iIndex));
274 AssertPtrReturnVoid(pPage);
275 m_pLabelPageTitle->setText(pPage->title());
276 if (iIndex > m_iLastIndex)
277 pPage->initializePage();
278
279 /* Disable/enable Next button: */
280 QPushButton *pButtonNext = wizardButton(WizardButtonType_Next);
281 AssertMsgReturnVoid(pButtonNext, ("No Next wizard button found!\n"));
282 pButtonNext->setEnabled(pPage->isComplete());
283
284 /* Update last index: */
285 m_iLastIndex = iIndex;
286}
287
288void UINativeWizard::sltCompleteChanged()
289{
290 /* Make sure sender is current widget: */
291 QWidget *pSender = qobject_cast<QWidget*>(sender());
292 if (pSender != m_pWidgetStack->currentWidget())
293 return;
294
295 /* Allow Next button only if current page is complete: */
296 UINativeWizardPage *pPage = qobject_cast<UINativeWizardPage*>(pSender);
297 QPushButton *pButtonNext = wizardButton(WizardButtonType_Next);
298 AssertMsgReturnVoid(pButtonNext, ("No Next wizard button found!\n"));
299 pButtonNext->setEnabled(pPage->isComplete());
300}
301
302void UINativeWizard::sltExpert()
303{
304 /* Toggle mode: */
305 switch (m_enmMode)
306 {
307 case WizardMode_Basic: m_enmMode = WizardMode_Expert; break;
308 case WizardMode_Expert: m_enmMode = WizardMode_Basic; break;
309 default: AssertMsgFailed(("Invalid mode: %d", m_enmMode)); break;
310 }
311 gEDataManager->setModeForWizardType(m_enmType, m_enmMode);
312
313 /* Reinit everything: */
314 deinit();
315 init();
316}
317
318void UINativeWizard::sltPrevious()
319{
320 /* For all allowed pages besides the 1st one we going backward: */
321 bool fPreviousFound = false;
322 int iIteratedIndex = m_pWidgetStack->currentIndex();
323 while (!fPreviousFound && iIteratedIndex > 0)
324 if (isPageVisible(--iIteratedIndex))
325 fPreviousFound = true;
326 if (fPreviousFound)
327 m_pWidgetStack->setCurrentIndex(iIteratedIndex);
328}
329
330void UINativeWizard::sltNext()
331{
332 /* Look for Next button: */
333 QPushButton *pButtonNext = wizardButton(WizardButtonType_Next);
334 AssertMsgReturnVoid(pButtonNext, ("No Next wizard button found!\n"));
335
336 /* Validate page before going forward: */
337 AssertReturnVoid(m_pWidgetStack->currentIndex() < m_pWidgetStack->count());
338 UINativeWizardPage *pPage = qobject_cast<UINativeWizardPage*>(m_pWidgetStack->currentWidget());
339 AssertPtrReturnVoid(pPage);
340 pButtonNext->setEnabled(false);
341 const bool fIsPageValid = pPage->validatePage();
342 pButtonNext->setEnabled(true);
343 if (!fIsPageValid)
344 return;
345
346 /* For all allowed pages besides the last one we going forward: */
347 bool fNextFound = false;
348 int iIteratedIndex = m_pWidgetStack->currentIndex();
349 while (!fNextFound && iIteratedIndex < m_pWidgetStack->count() - 1)
350 if (isPageVisible(++iIteratedIndex))
351 fNextFound = true;
352 if (fNextFound)
353 m_pWidgetStack->setCurrentIndex(iIteratedIndex);
354 /* For last one we just accept the wizard: */
355 else
356 accept();
357}
358
359void UINativeWizard::prepare()
360{
361 /* Prepare main layout: */
362 QVBoxLayout *pLayoutMain = new QVBoxLayout(this);
363 if (pLayoutMain)
364 {
365 /* No need for margins and spacings between sub-layouts: */
366 pLayoutMain->setContentsMargins(0, 0, 0, 0);
367 pLayoutMain->setSpacing(0);
368
369 /* Prepare upper layout: */
370 QHBoxLayout *pLayoutUpper = new QHBoxLayout;
371 if (pLayoutUpper)
372 {
373#ifdef VBOX_WS_MAC
374 /* No need for bottom margin on macOS, reseting others to default: */
375 const int iL = qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin);
376 const int iT = qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin);
377 const int iR = qApp->style()->pixelMetric(QStyle::PM_LayoutRightMargin);
378 pLayoutUpper->setContentsMargins(iL, iT, iR, 0);
379#endif /* VBOX_WS_MAC */
380 /* Reset spacing to default, it was flawed by parent inheritance: */
381 const int iSpacing = qApp->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing);
382 pLayoutUpper->setSpacing(iSpacing);
383
384 /* Prepare pixmap label: */
385 m_pLabelPixmap = new QLabel(this);
386 if (m_pLabelPixmap)
387 {
388 m_pLabelPixmap->setAlignment(Qt::AlignTop);
389#ifdef VBOX_WS_MAC
390 /* On macOS this label contains background, which isn't a part of layout, moving manually: */
391 m_pLabelPixmap->move(0, 0);
392 /* Spacer to make look&feel native on macOS: */
393 QSpacerItem *pSpacer = new QSpacerItem(200, 0, QSizePolicy::Fixed, QSizePolicy::Minimum);
394 pLayoutUpper->addItem(pSpacer);
395#else /* !VBOX_WS_MAC */
396 /* Just add label into layout on other platforms: */
397 pLayoutUpper->addWidget(m_pLabelPixmap);
398#endif /* !VBOX_WS_MAC */
399 }
400
401 /* Prepare right layout: */
402 m_pLayoutRight = new QVBoxLayout;
403 if (m_pLayoutRight)
404 {
405 /* Prepare page title label: */
406 m_pLabelPageTitle = new QLabel(this);
407 if (m_pLabelPageTitle)
408 {
409 /* Title should have big/fat font: */
410 QFont labelFont = m_pLabelPageTitle->font();
411 labelFont.setBold(true);
412 labelFont.setPointSize(labelFont.pointSize() + 4);
413 m_pLabelPageTitle->setFont(labelFont);
414
415 m_pLayoutRight->addWidget(m_pLabelPageTitle);
416 }
417
418#ifdef VBOX_WS_MAC
419 /* Prepare frame around widget-stack on macOS for nativity purposes: */
420 UIFrame *pFrame = new UIFrame(this);
421 if (pFrame)
422 {
423 /* Prepare frame layout: */
424 QVBoxLayout *pLayoutFrame = new QVBoxLayout(pFrame);
425 if (pLayoutFrame)
426 {
427 /* Prepare widget-stack: */
428 m_pWidgetStack = new QStackedWidget(pFrame);
429 if (m_pWidgetStack)
430 {
431 connect(m_pWidgetStack, &QStackedWidget::currentChanged, this, &UINativeWizard::sltCurrentIndexChanged);
432 pLayoutFrame->addWidget(m_pWidgetStack);
433 }
434 }
435
436 /* Add to layout: */
437 m_pLayoutRight->addWidget(pFrame);
438 }
439#else /* !VBOX_WS_MAC */
440 /* Prepare widget-stack directly on other platforms: */
441 m_pWidgetStack = new QStackedWidget(this);
442 if (m_pWidgetStack)
443 {
444 connect(m_pWidgetStack, &QStackedWidget::currentChanged, this, &UINativeWizard::sltCurrentIndexChanged);
445 m_pLayoutRight->addWidget(m_pWidgetStack);
446 }
447#endif /* !VBOX_WS_MAC */
448
449 /* Add to layout: */
450 pLayoutUpper->addLayout(m_pLayoutRight);
451 }
452
453 /* Add to layout: */
454 pLayoutMain->addLayout(pLayoutUpper, 1);
455 }
456
457 /* Prepare bottom widget: */
458 QWidget *pWidgetBottom = new QWidget(this);
459 if (pWidgetBottom)
460 {
461#ifndef VBOX_WS_MAC
462 /* Adjust palette a bit on Windows/X11 for native purposes: */
463 pWidgetBottom->setAutoFillBackground(true);
464 QPalette pal = QGuiApplication::palette();
465 pal.setColor(QPalette::Active, QPalette::Window, pal.color(QPalette::Active, QPalette::Window).darker(110));
466 pal.setColor(QPalette::Inactive, QPalette::Window, pal.color(QPalette::Inactive, QPalette::Window).darker(110));
467 pWidgetBottom->setPalette(pal);
468#endif /* !VBOX_WS_MAC */
469
470 /* Prepare bottom layout: */
471 QHBoxLayout *pLayoutBottom = new QHBoxLayout(pWidgetBottom);
472 if (pLayoutBottom)
473 {
474 /* Reset margins to default, they were flawed by parent inheritance: */
475 const int iL = qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin);
476 const int iT = qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin);
477 const int iR = qApp->style()->pixelMetric(QStyle::PM_LayoutRightMargin);
478 const int iB = qApp->style()->pixelMetric(QStyle::PM_LayoutBottomMargin);
479 pLayoutBottom->setContentsMargins(iL, iT, iR, iB);
480
481 // WORKAROUND:
482 // Prepare dialog button-box? Huh, no .. QWizard has different opinion.
483 // So we are hardcoding order, same on all platforms, which is the case.
484 for (int i = WizardButtonType_Invalid + 1; i < WizardButtonType_Max; ++i)
485 {
486 const WizardButtonType enmType = (WizardButtonType)i;
487 /* Create Help button only if help hash tag is set.
488 * Create other buttons in any case: */
489 if (enmType != WizardButtonType_Help || !m_strHelpHashtag.isEmpty())
490 m_buttons[enmType] = new QPushButton(pWidgetBottom);
491 QPushButton *pButton = wizardButton(enmType);
492 if (pButton)
493 pLayoutBottom->addWidget(pButton);
494 if (enmType == WizardButtonType_Help)
495 pLayoutBottom->addStretch(1);
496 if ( pButton
497 && enmType == WizardButtonType_Next)
498 pButton->setDefault(true);
499 }
500 /* Connect buttons: */
501 if (wizardButton(WizardButtonType_Help))
502 {
503 connect(wizardButton(WizardButtonType_Help), &QPushButton::clicked,
504 &(msgCenter()), &UIMessageCenter::sltHandleHelpRequest);
505 wizardButton(WizardButtonType_Help)->setShortcut(QKeySequence::HelpContents);
506 uiCommon().setHelpKeyword(wizardButton(WizardButtonType_Help), m_strHelpHashtag);
507 }
508 connect(wizardButton(WizardButtonType_Expert), &QPushButton::clicked,
509 this, &UINativeWizard::sltExpert);
510 connect(wizardButton(WizardButtonType_Back), &QPushButton::clicked,
511 this, &UINativeWizard::sltPrevious);
512 connect(wizardButton(WizardButtonType_Next), &QPushButton::clicked,
513 this, &UINativeWizard::sltNext);
514 connect(wizardButton(WizardButtonType_Cancel), &QPushButton::clicked,
515 this, &UINativeWizard::reject);
516 }
517
518 /* Add to layout: */
519 pLayoutMain->addWidget(pWidgetBottom);
520 }
521 }
522
523 /* Prepare local notification-center: */
524 m_pNotificationCenter = new UINotificationCenter(this);
525}
526
527void UINativeWizard::cleanup()
528{
529 /* Cleanup local notification-center: */
530 delete m_pNotificationCenter;
531 m_pNotificationCenter = 0;
532}
533
534void UINativeWizard::init()
535{
536 /* Populate pages: */
537 populatePages();
538
539 /* Translate wizard: */
540 retranslateUi();
541 /* Translate wizard pages: */
542 retranslatePages();
543
544 /* Resize wizard to 'golden ratio': */
545 resizeToGoldenRatio();
546
547 /* Make sure current page initialized: */
548 sltCurrentIndexChanged();
549}
550
551void UINativeWizard::deinit()
552{
553 /* Remove all the pages: */
554 m_pWidgetStack->blockSignals(true);
555 while (m_pWidgetStack->count() > 0)
556 {
557 QWidget *pLastWidget = m_pWidgetStack->widget(m_pWidgetStack->count() - 1);
558 m_pWidgetStack->removeWidget(pLastWidget);
559 delete pLastWidget;
560 }
561 m_pWidgetStack->blockSignals(false);
562
563 /* Update last index: */
564 m_iLastIndex = -1;
565 /* Update invisible pages: */
566 m_invisiblePages.clear();
567
568 /* Clean wizard finally: */
569 cleanWizard();
570}
571
572void UINativeWizard::retranslatePages()
573{
574 /* Translate all the pages: */
575 for (int i = 0; i < m_pWidgetStack->count(); ++i)
576 qobject_cast<UINativeWizardPage*>(m_pWidgetStack->widget(i))->retranslate();
577}
578
579void UINativeWizard::resizeToGoldenRatio()
580{
581 /* Standard top margin: */
582 const int iT = qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin);
583 m_pLayoutRight->setContentsMargins(0, iT, 0, 0);
584 /* Show title label for Basic mode case: */
585 m_pLabelPageTitle->setVisible(m_enmMode == WizardMode_Basic);
586#ifndef VBOX_WS_MAC
587 /* Hide/show pixmap label on Windows/X11 only, on macOS it's in the background: */
588 m_pLabelPixmap->setVisible(!m_strPixmapName.isEmpty());
589#endif /* !VBOX_WS_MAC */
590
591 /* For wizard in Basic mode: */
592 if (m_enmMode == WizardMode_Basic)
593 {
594 /* Temporary hide all the QIRichTextLabel(s) to exclude
595 * influence onto m_pWidgetStack minimum size-hint below: */
596 foreach (QIRichTextLabel *pLabel, findChildren<QIRichTextLabel*>())
597 pLabel->hide();
598 /* Gather suitable dimensions: */
599 const int iStepWidth = 100;
600 const int iMinWidth = qMax(100, m_pWidgetStack->minimumSizeHint().width());
601 const int iMaxWidth = qMax(iMinWidth, gpDesktop->availableGeometry(this).width() * 3 / 4);
602 /* Show all the QIRichTextLabel(s) again, they were hidden above: */
603 foreach (QIRichTextLabel *pLabel, findChildren<QIRichTextLabel*>())
604 pLabel->show();
605 /* Now look for a golden ratio: */
606 int iCurrentWidth = iMinWidth;
607 do
608 {
609 /* Assign current QIRichTextLabel(s) width: */
610 foreach (QIRichTextLabel *pLabel, findChildren<QIRichTextLabel*>())
611 pLabel->setMinimumTextWidth(iCurrentWidth);
612
613 /* Calculate current ratio: */
614 const QSize msh = m_pWidgetStack->minimumSizeHint();
615 int iWidth = msh.width();
616 int iHeight = msh.height();
617#ifndef VBOX_WS_MAC
618 /* Advance width for standard watermark width: */
619 if (!m_strPixmapName.isEmpty())
620 iWidth += 145;
621 /* Advance height for spacing & title height: */
622 if (m_pLayoutRight)
623 {
624 int iL, iT, iR, iB;
625 m_pLayoutRight->getContentsMargins(&iL, &iT, &iR, &iB);
626 iHeight += iT + m_pLayoutRight->spacing() + iB;
627 }
628 if (m_pLabelPageTitle)
629 iHeight += m_pLabelPageTitle->minimumSizeHint().height();
630#endif /* !VBOX_WS_MAC */
631 const double dRatio = (double)iWidth / iHeight;
632 if (dRatio > 1.6)
633 break;
634
635 /* Advance current width: */
636 iCurrentWidth += iStepWidth;
637 }
638 while (iCurrentWidth < iMaxWidth);
639 }
640
641#ifdef VBOX_WS_MAC
642 /* Assign background finally: */
643 if (!m_strPixmapName.isEmpty())
644 assignBackground();
645#else
646 /* Assign watermark finally: */
647 if (!m_strPixmapName.isEmpty())
648 assignWatermark();
649#endif /* !VBOX_WS_MAC */
650
651 /* Make sure layouts are freshly updated & activated: */
652 foreach (QLayout *pLayout, findChildren<QLayout*>())
653 {
654 pLayout->update();
655 pLayout->activate();
656 }
657 QCoreApplication::sendPostedEvents(0, QEvent::LayoutRequest);
658
659 /* Resize to minimum size-hint: */
660 resize(minimumSizeHint());
661}
662
663bool UINativeWizard::isLastVisiblePage(int iPageIndex) const
664{
665 if (!m_pWidgetStack)
666 return false;
667 if (iPageIndex == -1)
668 return false;
669 /* The page itself is not visible: */
670 if (m_invisiblePages.contains(iPageIndex))
671 return false;
672 bool fLastVisible = true;
673 /* Look at the page coming after the page with @p iPageIndex and check if they are visible: */
674 for (int i = iPageIndex + 1; i < m_pWidgetStack->count(); ++i)
675 {
676 if (!m_invisiblePages.contains(i))
677 {
678 fLastVisible = false;
679 break;
680 }
681 }
682 return fLastVisible;
683}
684
685#ifdef VBOX_WS_MAC
686void UINativeWizard::assignBackground()
687{
688 /* Load pixmap to icon first, this will gather HiDPI pixmaps as well: */
689 const QIcon icon = UIIconPool::iconSet(m_strPixmapName);
690
691 /* Acquire pixmap of required size and scale (on basis of parent-widget's device pixel ratio): */
692 const QSize standardSize(620, 440);
693 const QPixmap pixmapOld = icon.pixmap(parentWidget()->windowHandle(), standardSize);
694
695 /* Assign background finally: */
696 m_pLabelPixmap->setPixmap(pixmapOld);
697 m_pLabelPixmap->resize(m_pLabelPixmap->minimumSizeHint());
698}
699
700#else
701
702void UINativeWizard::assignWatermark()
703{
704 /* Load pixmap to icon first, this will gather HiDPI pixmaps as well: */
705 const QIcon icon = UIIconPool::iconSet(m_strPixmapName);
706
707 /* Acquire pixmap of required size and scale (on basis of parent-widget's device pixel ratio): */
708 const QSize standardSize(145, 290);
709 const QPixmap pixmapOld = icon.pixmap(parentWidget()->windowHandle(), standardSize);
710
711 /* Convert watermark to image which allows to manage pixel data directly: */
712 const QImage imageOld = pixmapOld.toImage();
713 /* Use the right-top watermark pixel as frame color: */
714 const QRgb rgbFrame = imageOld.pixel(imageOld.width() - 1, 0);
715
716 /* Compose desired height up to pixmap device pixel ratio: */
717 int iL, iT, iR, iB;
718 m_pLayoutRight->getContentsMargins(&iL, &iT, &iR, &iB);
719 const int iSpacing = iT + m_pLayoutRight->spacing() + iB;
720 const int iTitleHeight = m_pLabelPageTitle->minimumSizeHint().height();
721 const int iStackHeight = m_pWidgetStack->minimumSizeHint().height();
722 const int iDesiredHeight = (iTitleHeight + iSpacing + iStackHeight) * pixmapOld.devicePixelRatio();
723 /* Create final image on the basis of incoming, applying the rules: */
724 QImage imageNew(imageOld.width(), qMax(imageOld.height(), iDesiredHeight), imageOld.format());
725 for (int y = 0; y < imageNew.height(); ++y)
726 {
727 for (int x = 0; x < imageNew.width(); ++x)
728 {
729 /* Border rule: */
730 if (x == imageNew.width() - 1)
731 imageNew.setPixel(x, y, rgbFrame);
732 /* Horizontal extension rule - use last used color: */
733 else if (x >= imageOld.width() && y < imageOld.height())
734 imageNew.setPixel(x, y, imageOld.pixel(imageOld.width() - 1, y));
735 /* Vertical extension rule - use last used color: */
736 else if (y >= imageOld.height() && x < imageOld.width())
737 imageNew.setPixel(x, y, imageOld.pixel(x, imageOld.height() - 1));
738 /* Common extension rule - use last used color: */
739 else if (x >= imageOld.width() && y >= imageOld.height())
740 imageNew.setPixel(x, y, imageOld.pixel(imageOld.width() - 1, imageOld.height() - 1));
741 /* Else just copy color: */
742 else
743 imageNew.setPixel(x, y, imageOld.pixel(x, y));
744 }
745 }
746
747 /* Convert processed image to pixmap: */
748 QPixmap pixmapNew = QPixmap::fromImage(imageNew);
749 /* For HiDPI support parent-widget's device pixel ratio is to be taken into account: */
750 double dRatio = 1.0;
751 if ( parentWidget()
752 && parentWidget()->window()
753 && parentWidget()->window()->windowHandle())
754 dRatio = parentWidget()->window()->windowHandle()->devicePixelRatio();
755 pixmapNew.setDevicePixelRatio(dRatio);
756 /* Assign watermark finally: */
757 m_pLabelPixmap->setPixmap(pixmapNew);
758}
759
760#endif /* !VBOX_WS_MAC */
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