VirtualBox

Ignore:
Timestamp:
Jun 29, 2021 5:49:15 PM (4 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
145418
Message:

FE/Qt: bugref:9996: Native wizard initial implementation.

Location:
trunk/src/VBox/Frontends/VirtualBox
Files:
2 edited
4 copied

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Frontends/VirtualBox/Makefile.kmk

    r89815 r89960  
    903903        src/widgets/UIToolBox.h \
    904904        src/widgets/UIWarningPane.h \
     905        src/wizards/UINativeWizard.h \
     906        src/wizards/UINativeWizardPage.h \
    905907        src/wizards/UIWizard.h \
    906908        src/wizards/UIWizardPage.h \
     
    14451447        src/widgets/UIToolBox.cpp \
    14461448        src/widgets/UIWarningPane.cpp \
     1449        src/wizards/UINativeWizard.cpp \
     1450        src/wizards/UINativeWizardPage.cpp \
    14471451        src/wizards/UIWizard.cpp \
    14481452        src/wizards/UIWizardPage.cpp \
  • trunk/src/VBox/Frontends/VirtualBox/src/globals/QIWithRetranslateUI.h

    r87718 r89960  
    2424/* Qt includes: */
    2525#include <QApplication>
     26#include <QDialog>
    2627#include <QEvent>
    2728#include <QGraphicsWidget>
     
    6768};
    6869
    69 /** Explicit QIWithRetranslateUI instantiation for QWidget class.
     70/** Explicit QIWithRetranslateUI instantiation for QWidget & QDialog classes.
    7071  * @note  On Windows it's important that all template cases are instantiated just once across
    7172  *        the linking space. In case we have particular template case instantiated from both
     
    7576  *        to library because latter can have lack of required instantiations (current case). */
    7677template class SHARED_LIBRARY_STUFF QIWithRetranslateUI<QWidget>;
     78template class SHARED_LIBRARY_STUFF QIWithRetranslateUI<QDialog>;
    7779
    7880
  • trunk/src/VBox/Frontends/VirtualBox/src/wizards/UINativeWizard.cpp

    r89882 r89960  
    11/* $Id$ */
    22/** @file
    3  * VBox Qt GUI - UIWizard class implementation.
     3 * VBox Qt GUI - UINativeWizard class implementation.
    44 */
    55
    66/*
    7  * Copyright (C) 2009-2020 Oracle Corporation
     7 * Copyright (C) 2009-2021 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    1717
    1818/* Qt includes: */
    19 #include <QAbstractButton>
    20 #include <QLayout>
     19#include <QHBoxLayout>
     20#include <QLabel>
     21#include <QPainter>
     22#include <QPushButton>
     23#include <QStackedWidget>
    2124#include <QStyle>
     25#include <QVBoxLayout>
    2226#include <QWindow>
    2327
    2428/* GUI includes: */
     29#include "QIRichTextLabel.h"
     30#include "UICommon.h"
     31#include "UIDesktopWidgetWatchdog.h"
     32#include "UIExtraDataManager.h"
    2533#include "UIIconPool.h"
    26 #include "UIWizard.h"
    27 #include "UIWizardPage.h"
    28 #include "UICommon.h"
    2934#include "UIMessageCenter.h"
    30 #include "QIRichTextLabel.h"
    31 #include "UIExtraDataManager.h"
    32 
    33 /* Qt includes: */
    34 #include <QtMath>
    35 
    36 
    37 void UIWizard::prepare()
    38 {
    39     // WORKAROUND:
    40     // In Qt 5.15 setting fusion application style leaves wizard style undetermined (unset).
    41     // But there is no "unset" enum value, so it's kinda 0, which means QWizard::ClassicStyle.
    42     // But by the fact wizard doesn't get rendered as QWizard::ClassicStyle, layout is broken.
    43     // So, we are forcing QWizard::ClassicStyle ourselves ..
    44     if (wizardStyle() == QWizard::ClassicStyle)
    45         setWizardStyle(QWizard::ClassicStyle);
     35#include "UINativeWizard.h"
     36#include "UINativeWizardPage.h"
     37
     38
     39#ifdef VBOX_WS_MAC
     40UIFrame::UIFrame(QWidget *pParent)
     41    : QWidget(pParent)
     42{
     43}
     44
     45void UIFrame::paintEvent(QPaintEvent *pEvent)
     46{
     47    /* Prepare painter: */
     48    QPainter painter(this);
     49
     50    /* Limit painting with incoming rectangle: */
     51    painter.setClipRect(pEvent->rect());
     52
     53    /* Check whether we should use Active or Inactive palette: */
     54    const bool fActive = parentWidget() && parentWidget()->isActiveWindow();
     55
     56    /* Paint background: */
     57    QColor backgroundColor = QGuiApplication::palette().color(fActive ? QPalette::Active : QPalette::Inactive, QPalette::Window);
     58    backgroundColor.setAlpha(100);
     59    painter.setPen(backgroundColor);
     60    painter.setBrush(backgroundColor);
     61    painter.drawRect(rect());
     62
     63    /* Paint borders: */
     64    painter.setPen(QGuiApplication::palette().color(fActive ? QPalette::Active : QPalette::Inactive, QPalette::Window).darker(130));
     65    QLine line1(0,                  0,                      rect().width() - 1, 0);
     66    QLine line2(rect().width() - 1, 0,                      rect().width() - 1, rect().height() - 1);
     67    QLine line3(rect().width() - 1, rect().height() - 1, 0,                     rect().height() - 1);
     68    QLine line4(0,                  rect().height() - 1, 0,                     0);
     69    painter.drawLine(line1);
     70    painter.drawLine(line2);
     71    painter.drawLine(line3);
     72    painter.drawLine(line4);
     73}
     74#endif /* VBOX_WS_MAC */
     75
     76
     77int UINativeWizard::exec()
     78{
     79    /* Init wizard: */
     80    init();
     81
     82    /* Call to base-class: */
     83    return QIWithRetranslateUI<QDialog>::exec();
     84}
     85
     86UINativeWizard::UINativeWizard(QWidget *pParent,
     87                               WizardType enmType,
     88                               WizardMode enmMode /* = WizardMode_Auto */,
     89                               const QString &strHelpHashtag /* = QString() */)
     90    : QIWithRetranslateUI<QDialog>(pParent)
     91    , m_enmType(enmType)
     92    , m_enmMode(enmMode == WizardMode_Auto ? gEDataManager->modeForWizardType(m_enmType) : enmMode)
     93    , m_strHelpHashtag(strHelpHashtag)
     94    , m_pLabelPixmap(0)
     95#ifdef VBOX_WS_MAC
     96    , m_pLabelPageTitle(0)
     97#endif
     98    , m_pWidgetStack(0)
     99{
     100    prepare();
     101}
     102
     103QPushButton *UINativeWizard::wizardButton(const WizardButtonType &enmType) const
     104{
     105    return m_buttons.value(enmType);
     106}
     107
     108void UINativeWizard::setPixmapName(const QString &strName)
     109{
     110    m_strPixmapName = strName;
     111}
     112
     113int UINativeWizard::addPage(UINativeWizardPage *pPage)
     114{
     115    /* Adjust page layout: */
     116    const int iL = m_enmMode == WizardMode_Expert
     117                 ? qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin)
     118                 : 0;
     119    const int iT = m_enmMode == WizardMode_Expert
     120                 ? qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin)
     121                 : 0;
     122    const int iR = qApp->style()->pixelMetric(QStyle::PM_LayoutRightMargin);
     123    const int iB = qApp->style()->pixelMetric(QStyle::PM_LayoutBottomMargin);
     124    pPage->layout()->setContentsMargins(iL, iT, iR, iB);
     125
     126    /* Add page to wizard's stack: */
     127    m_pWidgetStack->blockSignals(true);
     128    const int iIndex = m_pWidgetStack->addWidget(pPage);
     129    m_pWidgetStack->blockSignals(false);
     130
     131    /* Make sure wizard is aware of page validity changes: */
     132    connect(pPage, &UINativeWizardPage::completeChanged,
     133            this, &UINativeWizard::sltCompleteChanged);
     134
     135    /* Returns added page index: */
     136    return iIndex;
     137}
     138
     139void UINativeWizard::retranslateUi()
     140{
     141    /* Translate Help button: */
     142    QPushButton *pButtonHelp = wizardButton(WizardButtonType_Help);
     143    AssertMsgReturnVoid(pButtonHelp, ("No Help wizard button found!\n"));
     144    pButtonHelp->setText(tr("&Help"));
     145    pButtonHelp->setToolTip(tr("Open corresponding Help topic."));
     146
     147    /* Translate basic/expert button: */
     148    QPushButton *pButtonExpert = wizardButton(WizardButtonType_Expert);
     149    AssertMsgReturnVoid(pButtonExpert, ("No Expert wizard button found!\n"));
     150    switch (m_enmMode)
     151    {
     152        case WizardMode_Basic:
     153            pButtonExpert->setText(tr("&Expert Mode"));
     154            pButtonExpert->setToolTip(tr("Switch to <nobr><b>Expert Mode</b></nobr>, "
     155                                         "a one-page dialog for experienced users."));
     156            break;
     157        case WizardMode_Expert:
     158            pButtonExpert->setText(tr("&Guided Mode"));
     159            pButtonExpert->setToolTip(tr("Switch to <nobr><b>Guided Mode</b></nobr>, "
     160                                         "a step-by-step dialog with detailed explanations."));
     161            break;
     162        default:
     163            AssertMsgFailed(("Invalid wizard mode: %d", m_enmMode));
     164            break;
     165    }
     166
     167    /* Translate Back button: */
     168    QPushButton *pButtonBack = wizardButton(WizardButtonType_Back);
     169    AssertMsgReturnVoid(pButtonBack, ("No Back wizard button found!\n"));
     170    pButtonBack->setText(tr("&Back"));
     171    pButtonBack->setToolTip(tr("Go to previous wizard page."));
     172
     173    /* Translate Next button: */
     174    QPushButton *pButtonNext = wizardButton(WizardButtonType_Next);
     175    AssertMsgReturnVoid(pButtonNext, ("No Next wizard button found!\n"));
     176    if (m_pWidgetStack && m_pWidgetStack->currentIndex() < m_pWidgetStack->count() - 1)
     177    {
     178        pButtonNext->setText(tr("&Next"));
     179        pButtonNext->setToolTip(tr("Go to next wizard page."));
     180    }
     181    else
     182    {
     183        pButtonNext->setText(tr("&Finish"));
     184        pButtonNext->setToolTip(tr("Commit all wizard data."));
     185    }
     186
     187    /* Translate Cancel button: */
     188    QPushButton *pButtonCancel = wizardButton(WizardButtonType_Cancel);
     189    AssertMsgReturnVoid(pButtonCancel, ("No Cancel wizard button found!\n"));
     190    pButtonCancel->setText(tr("&Cancel"));
     191    pButtonCancel->setToolTip(tr("Cancel wizard execution."));
     192}
     193
     194void UINativeWizard::sltCurrentIndexChanged(int iIndex /* = -1 */)
     195{
     196    /* Update translation: */
     197    retranslateUi();
     198
     199    /* Sanity check: */
     200    AssertPtrReturnVoid(m_pWidgetStack);
     201
     202    /* -1 means current one page: */
     203    if (iIndex == -1)
     204        iIndex = m_pWidgetStack->currentIndex();
     205
     206    /* Hide/show Expert button (hidden by default): */
     207    bool fIsExpertButtonAvailable = false;
     208    /* Show Expert button for 1st page: */
     209    if (iIndex == 0)
     210        fIsExpertButtonAvailable = true;
     211    /* But first-run wizard has no such button anyway: */
     212    if (m_enmType == WizardType_FirstRun)
     213        fIsExpertButtonAvailable = false;
     214    /* Hide/show Expert button finally: */
     215    QPushButton *pButtonExpert = wizardButton(WizardButtonType_Expert);
     216    AssertMsgReturnVoid(pButtonExpert, ("No Expert wizard button found!\n"));
     217    pButtonExpert->setVisible(fIsExpertButtonAvailable);
     218
     219    /* Disable/enable Back button: */
     220    QPushButton *pButtonBack = wizardButton(WizardButtonType_Back);
     221    AssertMsgReturnVoid(pButtonBack, ("No Back wizard button found!\n"));
     222    pButtonBack->setEnabled(iIndex > 0);
     223
     224    /* Initialize corresponding page: */
     225    UINativeWizardPage *pPage = qobject_cast<UINativeWizardPage*>(m_pWidgetStack->widget(iIndex));
     226    AssertPtrReturnVoid(pPage);
     227#ifdef VBOX_WS_MAC
     228    m_pLabelPageTitle->setText(pPage->title());
     229#endif
     230    pPage->initializePage();
     231}
     232
     233void UINativeWizard::sltCompleteChanged()
     234{
     235    /* Make sure sender is current widget: */
     236    QWidget *pSender = qobject_cast<QWidget*>(sender());
     237    AssertReturnVoid(m_pWidgetStack->currentWidget() == pSender);
     238
     239    /* Allow Next button only if current page is complete: */
     240    UINativeWizardPage *pPage = qobject_cast<UINativeWizardPage*>(pSender);
     241    QPushButton *pButtonNext = wizardButton(WizardButtonType_Next);
     242    AssertMsgReturnVoid(pButtonNext, ("No Next wizard button found!\n"));
     243    pButtonNext->setEnabled(pPage->isComplete());
     244}
     245
     246void UINativeWizard::sltExpert()
     247{
     248    /* Toggle mode: */
     249    switch (m_enmMode)
     250    {
     251        case WizardMode_Basic:  m_enmMode = WizardMode_Expert; break;
     252        case WizardMode_Expert: m_enmMode = WizardMode_Basic;  break;
     253        default: AssertMsgFailed(("Invalid mode: %d", m_enmMode)); break;
     254    }
     255    gEDataManager->setModeForWizardType(m_enmType, m_enmMode);
     256
     257    /* Cleanup/init again: */
     258    cleanup();
     259    init();
     260}
     261
     262void UINativeWizard::sltPrevious()
     263{
     264    /* For all the pages besides the 1st one we going backward: */
     265    AssertReturnVoid(m_pWidgetStack->currentIndex() > 0);
     266    m_pWidgetStack->setCurrentIndex(m_pWidgetStack->currentIndex() - 1);
     267}
     268
     269void UINativeWizard::sltNext()
     270{
     271    /* Look for Next button: */
     272    QPushButton *pButtonNext = wizardButton(WizardButtonType_Next);
     273    AssertMsgReturnVoid(pButtonNext, ("No Next wizard button found!\n"));
     274
     275    /* Validate page before going forward: */
     276    AssertReturnVoid(m_pWidgetStack->currentIndex() < m_pWidgetStack->count());
     277    UINativeWizardPage *pPage = qobject_cast<UINativeWizardPage*>(m_pWidgetStack->currentWidget());
     278    AssertPtrReturnVoid(pPage);
     279    pButtonNext->setEnabled(false);
     280    const bool fIsPageValid = pPage->validatePage();
     281    pButtonNext->setEnabled(true);
     282    if (!fIsPageValid)
     283        return;
     284
     285    /* For all the pages besides the last one we going forward: */
     286    if (m_pWidgetStack->currentIndex() < m_pWidgetStack->count() - 1)
     287        m_pWidgetStack->setCurrentIndex(m_pWidgetStack->currentIndex() + 1);
     288    /* For last one we just accept the wizard: */
     289    else
     290        accept();
     291}
     292
     293void UINativeWizard::prepare()
     294{
     295    /* Prepare main layout: */
     296    QVBoxLayout *pLayoutMain = new QVBoxLayout(this);
     297    if (pLayoutMain)
     298    {
     299        /* No need for margins and spacings between sub-layouts: */
     300        pLayoutMain->setContentsMargins(0, 0, 0, 0);
     301        pLayoutMain->setSpacing(0);
     302
     303        /* Prepare upper layout: */
     304        QHBoxLayout *pLayoutUpper = new QHBoxLayout;
     305        if (pLayoutUpper)
     306        {
     307#ifdef VBOX_WS_MAC
     308            /* No need for bottom margin on macOS, reseting others to default: */
     309            const int iL = qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin);
     310            const int iT = qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin);
     311            const int iR = qApp->style()->pixelMetric(QStyle::PM_LayoutRightMargin);
     312            pLayoutUpper->setContentsMargins(iL, iT, iR, 0);
     313#endif /* VBOX_WS_MAC */
     314            /* Reset spacing to default, it was flawed by parent inheritance: */
     315            const int iSpacing = qApp->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing);
     316            pLayoutUpper->setSpacing(iSpacing);
     317
     318            /* Prepare pixmap label: */
     319            m_pLabelPixmap = new QLabel(this);
     320            if (m_pLabelPixmap)
     321            {
     322                m_pLabelPixmap->setAlignment(Qt::AlignTop);
     323#ifdef VBOX_WS_MAC
     324                /* On macOS this label contains background, which isn't a part of layout, moving manually: */
     325                m_pLabelPixmap->move(0, 0);
     326                /* Spacer to make look&feel native on macOS: */
     327                QSpacerItem *pSpacer = new QSpacerItem(200, 0, QSizePolicy::Fixed, QSizePolicy::Minimum);
     328                pLayoutUpper->addItem(pSpacer);
     329#else /* !VBOX_WS_MAC */
     330                /* Just add label into layout on other platforms: */
     331                pLayoutUpper->addWidget(m_pLabelPixmap);
     332#endif /* !VBOX_WS_MAC */
     333            }
     334
     335#ifdef VBOX_WS_MAC
     336            /* Prepare right layout on macOS for nativity purposes: */
     337            QVBoxLayout *pLayoutRight = new QVBoxLayout;
     338            if (pLayoutRight)
     339            {
     340                /* Prepare page title label: */
     341                m_pLabelPageTitle = new QLabel(this);
     342                if (m_pLabelPageTitle)
     343                {
     344                    /* Title should have big/fat font: */
     345                    QFont labelFont = m_pLabelPageTitle->font();
     346                    labelFont.setBold(true);
     347                    labelFont.setPointSize(labelFont.pointSize() + 4);
     348                    m_pLabelPageTitle->setFont(labelFont);
     349
     350                    pLayoutRight->addWidget(m_pLabelPageTitle);
     351                }
     352
     353                /* Prepare frame around widget-stack on macOS for nativity purposes: */
     354                UIFrame *pFrame = new UIFrame(this);
     355                if (pFrame)
     356                {
     357                    /* Prepare frame layout: */
     358                    QVBoxLayout *pLayoutFrame = new QVBoxLayout(pFrame);
     359                    if (pLayoutFrame)
     360                    {
     361                        /* Prepare widget-stack: */
     362                        m_pWidgetStack = new QStackedWidget(pFrame);
     363                        if (m_pWidgetStack)
     364                        {
     365                            connect(m_pWidgetStack, &QStackedWidget::currentChanged, this, &UINativeWizard::sltCurrentIndexChanged);
     366                            pLayoutFrame->addWidget(m_pWidgetStack);
     367                        }
     368                    }
     369
     370                    /* Add to layout: */
     371                    pLayoutRight->addWidget(pFrame);
     372                }
     373
     374                /* Add to layout: */
     375                pLayoutUpper->addLayout(pLayoutRight);
     376            }
     377#else /* !VBOX_WS_MAC */
     378            /* Prepare widget-stack directly on other platforms: */
     379            m_pWidgetStack = new QStackedWidget(this);
     380            if (m_pWidgetStack)
     381            {
     382                connect(m_pWidgetStack, &QStackedWidget::currentChanged, this, &UINativeWizard::sltCurrentIndexChanged);
     383                pLayoutUpper->addWidget(m_pWidgetStack);
     384            }
     385#endif /* !VBOX_WS_MAC */
     386
     387            /* Add to layout: */
     388            pLayoutMain->addLayout(pLayoutUpper);
     389        }
     390
     391        /* Prepare bottom widget: */
     392        QWidget *pWidgetBottom = new QWidget(this);
     393        if (pWidgetBottom)
     394        {
     395#ifndef VBOX_WS_MAC
     396            /* Adjust palette a bit on Windows/X11 for native purposes: */
     397            pWidgetBottom->setAutoFillBackground(true);
     398            QPalette pal = QGuiApplication::palette();
     399            pal.setColor(QPalette::Active, QPalette::Window, pal.color(QPalette::Active, QPalette::Window).darker(110));
     400            pal.setColor(QPalette::Inactive, QPalette::Window, pal.color(QPalette::Inactive, QPalette::Window).darker(110));
     401            pWidgetBottom->setPalette(pal);
     402#endif /* !VBOX_WS_MAC */
     403
     404            /* Prepare bottom layout: */
     405            QHBoxLayout *pLayoutBottom = new QHBoxLayout(pWidgetBottom);
     406            if (pLayoutBottom)
     407            {
     408                /* Reset margins to default, they were flawed by parent inheritance: */
     409                const int iL = qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin);
     410                const int iT = qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin);
     411                const int iR = qApp->style()->pixelMetric(QStyle::PM_LayoutRightMargin);
     412                const int iB = qApp->style()->pixelMetric(QStyle::PM_LayoutBottomMargin);
     413                pLayoutBottom->setContentsMargins(iL, iT, iR, iB);
     414
     415                // WORKAROUND:
     416                // Prepare dialog button-box? Huh, no .. QWizard has different opinion.
     417                // So we are hardcoding order, same on all platforms, which is the case.
     418                for (int i = WizardButtonType_Invalid + 1; i < WizardButtonType_Max; ++i)
     419                {
     420                    const WizardButtonType enmType = (WizardButtonType)i;
     421                    m_buttons[enmType] = new QPushButton(pWidgetBottom);
     422                    QPushButton *pButton = wizardButton(enmType);
     423                    if (pButton)
     424                        pLayoutBottom->addWidget(pButton);
     425                    if (enmType == WizardButtonType_Help)
     426                        pLayoutBottom->addStretch(1);
     427                    if (   pButton
     428                        && enmType == WizardButtonType_Next)
     429                        pButton->setDefault(true);
     430                }
     431                /* Connect buttons: */
     432                connect(wizardButton(WizardButtonType_Help), &QPushButton::pressed,
     433                        &(msgCenter()), &UIMessageCenter::sltHandleHelpRequest);
     434                wizardButton(WizardButtonType_Help)->setShortcut(QKeySequence::HelpContents);
     435                uiCommon().setHelpKeyword(wizardButton(WizardButtonType_Help), m_strHelpHashtag);
     436                connect(wizardButton(WizardButtonType_Expert), &QPushButton::pressed,
     437                        this, &UINativeWizard::sltExpert);
     438                connect(wizardButton(WizardButtonType_Back), &QPushButton::pressed,
     439                        this, &UINativeWizard::sltPrevious);
     440                connect(wizardButton(WizardButtonType_Next), &QPushButton::pressed,
     441                        this, &UINativeWizard::sltNext);
     442                connect(wizardButton(WizardButtonType_Cancel), &QPushButton::pressed,
     443                        this, &UINativeWizard::reject);
     444            }
     445
     446            /* Add to layout: */
     447            pLayoutMain->addWidget(pWidgetBottom);
     448        }
     449    }
     450}
     451
     452void UINativeWizard::cleanup()
     453{
     454    /* Remove all the pages: */
     455    m_pWidgetStack->blockSignals(true);
     456    while (m_pWidgetStack->count() > 0)
     457    {
     458        QWidget *pLastWidget = m_pWidgetStack->widget(m_pWidgetStack->count() - 1);
     459        m_pWidgetStack->removeWidget(pLastWidget);
     460        delete pLastWidget;
     461    }
     462    m_pWidgetStack->blockSignals(false);
     463}
     464
     465void UINativeWizard::init()
     466{
     467    /* Populate pages: */
     468    populatePages();
    46469
    47470    /* Translate wizard: */
     
    53476    resizeToGoldenRatio();
    54477
    55     /* Notify pages they are ready: */
    56     QList<int> ids = pageIds();
    57     for (int i = 0; i < ids.size(); ++i)
    58         qobject_cast<UIWizardPage*>(page(ids[i]))->markReady();
    59 
    60     /* Make sure custom buttons shown even if final page is first to show: */
    61     sltCurrentIdChanged(startId());
    62 }
    63 
    64 UIWizard::UIWizard(QWidget *pParent, WizardType enmType, WizardMode enmMode /* = WizardMode_Auto */)
    65     : QIWithRetranslateUI<QWizard>(pParent)
    66     , m_enmType(enmType)
    67     , m_enmMode(enmMode == WizardMode_Auto ? gEDataManager->modeForWizardType(m_enmType) : enmMode)
    68 {
    69 #ifdef VBOX_WS_WIN
    70     /* Hide window icon: */
    71     setWindowIcon(QIcon());
    72 #endif
    73 
    74 #ifdef VBOX_WS_MAC
     478    /* Make sure current page initialized: */
     479    sltCurrentIndexChanged();
     480}
     481
     482void UINativeWizard::retranslatePages()
     483{
     484    /* Translate all the pages: */
     485    for (int i = 0; i < m_pWidgetStack->count(); ++i)
     486        qobject_cast<UINativeWizardPage*>(m_pWidgetStack->widget(i))->retranslate();
     487}
     488
     489void UINativeWizard::resizeToGoldenRatio()
     490{
     491#ifdef VBOX_WS_MAC
     492    /* Hide/show title label on macOS only, on Windows/X11 there is no title label. */
     493    m_pLabelPageTitle->setVisible(m_enmMode == WizardMode_Basic);
     494#else
     495    /* Hide/show pixmap label on Windows/X11 only, on macOS it's in the background: */
     496    m_pLabelPixmap->setVisible(!m_strPixmapName.isEmpty() && m_enmMode == WizardMode_Basic);
     497#endif /* !VBOX_WS_MAC */
     498
     499    /* For wizard in Basic mode: */
     500    if (m_enmMode == WizardMode_Basic)
     501    {
     502        /* Temporary hide all the QIRichTextLabel(s) to exclude
     503         * influence onto m_pWidgetStack minimum size-hint below: */
     504        foreach (QIRichTextLabel *pLabel, findChildren<QIRichTextLabel*>())
     505            pLabel->hide();
     506        /* Gather suitable dimensions: */
     507        const int iStepWidth = 100;
     508        const int iMinWidth = qMax(100, m_pWidgetStack->minimumSizeHint().width());
     509        const int iMaxWidth = qMax(iMinWidth, gpDesktop->availableGeometry(this).width() * 3 / 4);
     510        /* Show all the QIRichTextLabel(s) again, they were hidden above: */
     511        foreach (QIRichTextLabel *pLabel, findChildren<QIRichTextLabel*>())
     512            pLabel->show();
     513        /* Now look for a golden ratio: */
     514        int iCurrentWidth = iMinWidth;
     515        do
     516        {
     517            /* Assign current QIRichTextLabel(s) width: */
     518            foreach (QIRichTextLabel *pLabel, findChildren<QIRichTextLabel*>())
     519                pLabel->setMinimumTextWidth(iCurrentWidth);
     520
     521            /* Calculate current ratio: */
     522            const QSize msh = m_pWidgetStack->minimumSizeHint();
     523            int iWidth = msh.width();
     524            int iHeight = msh.height();
     525#ifndef VBOX_WS_MAC
     526            /* Advance width for standard watermark width: */
     527            if (!m_strPixmapName.isEmpty())
     528                iWidth += 145;
     529#endif /* !VBOX_WS_MAC */
     530            const double dRatio = (double)iWidth / iHeight;
     531            if (dRatio > 1.6)
     532                break;
     533
     534            /* Advance current width: */
     535            iCurrentWidth += iStepWidth;
     536        }
     537        while (iCurrentWidth < iMaxWidth);
     538    }
     539
     540#ifdef VBOX_WS_MAC
     541    /* Assign background finally: */
     542    if (!m_strPixmapName.isEmpty())
     543        assignBackground();
     544#else
     545    /* Assign watermark finally: */
     546    if (   !m_strPixmapName.isEmpty()
     547        && m_enmMode == WizardMode_Basic)
     548        assignWatermark();
     549#endif /* !VBOX_WS_MAC */
     550
     551    /* Make sure layouts are freshly updated & activated: */
     552    foreach (QLayout *pLayout, findChildren<QLayout*>())
     553    {
     554        pLayout->update();
     555        pLayout->activate();
     556    }
     557#if defined(VBOX_WS_MAC) || defined(VBOX_WS_X11)
    75558    // WORKAROUND:
    76     // Since wizards are now represented as Mac OS X Sheets
    77     // we would like to have possibility to cancel them.
    78     setOption(QWizard::NoCancelButton, false);
    79 
    80     // WORKAROUND:
    81     // I'm really not sure why there shouldn't be any default button on Mac OS X.
    82     // This prevents the using of Enter to jump to the next page.
    83     setOptions(options() ^ QWizard::NoDefaultButton);
    84 #endif /* VBOX_WS_MAC */
    85 
    86     /* Using window-modality: */
    87     setWindowModality(Qt::WindowModal);
    88 
    89     /* Setup connections: */
    90     connect(this, &UIWizard::currentIdChanged,    this, &UIWizard::sltCurrentIdChanged);
    91     connect(this, &UIWizard::customButtonClicked, this, &UIWizard::sltCustomButtonClicked);
    92 }
    93 
    94 void UIWizard::retranslateUi()
    95 {
    96     /* Translate basic/expert button: */
    97     switch (m_enmMode)
    98     {
    99         case WizardMode_Basic:
    100             setButtonText(QWizard::CustomButton1, tr("&Expert Mode"));
    101             button(QWizard::CustomButton1)->setToolTip(tr("Switch to <nobr><b>Expert Mode</b></nobr>, a one-page dialog for experienced users."));
    102             break;
    103         case WizardMode_Expert:
    104             setButtonText(QWizard::CustomButton1, tr("&Guided Mode"));
    105             button(QWizard::CustomButton1)->setToolTip(tr("Switch to <nobr><b>Guided Mode</b></nobr>, a step-by-step dialog with detailed explanations."));
    106             break;
    107         default:
    108             AssertMsgFailed(("Invalid mode: %d", m_enmMode));
    109             break;
    110     }
    111 }
    112 
    113 void UIWizard::showEvent(QShowEvent *pEvent)
    114 {
    115     /* Resize to minimum possible size: */
    116     resize(0, 0);
    117 
    118     /* Call to base-class: */
    119     QWizard::showEvent(pEvent);
    120 }
    121 
    122 void UIWizard::setPage(int iId, UIWizardPage *pPage)
    123 {
    124     /* Configure page first: */
    125     configurePage(pPage);
    126     /* Add page finally: */
    127     QWizard::setPage(iId, pPage);
    128 }
    129 
    130 void UIWizard::cleanup()
    131 {
    132     /* Remove all the pages: */
    133     const QList<int> ids = pageIds();
    134     for (int i = ids.size() - 1; i >= 0 ; --i)
    135     {
    136         /* Get enumerated page ID: */
    137         const int iId = ids.at(i);
    138         /* Get corresponding page: */
    139         QWizardPage *pWizardPage = page(iId);
    140 
    141         /* Remove page from the wizard: */
    142         removePage(iId);
    143         /* Delete page finally: */
    144         delete pWizardPage;
    145     }
    146 
    147 #ifndef VBOX_WS_MAC
    148     /* Cleanup watermark: */
    149     if (!m_strWatermarkName.isEmpty())
    150         setPixmap(QWizard::WatermarkPixmap, QPixmap());
    151 #endif
    152 }
    153 
    154 void UIWizard::resizeToGoldenRatio()
    155 {
    156     /* Check if wizard is in basic or expert mode: */
    157     if (m_enmMode == WizardMode_Expert)
    158     {
    159         // WORKAROUND:
    160         // Unfortunately QWizard hides some of useful API in private part,
    161         // and also have few layouting bugs which could be easy fixed
    162         // by that API, so we will use QWizard::restart() method
    163         // to call the same functionality indirectly...
    164         // Early call restart() which is usually goes on show()!
    165         restart();
    166 
    167         // WORKAROUND:
    168         // Now we have correct label size-hint(s) for all the pages.
    169         // We have to make sure all the pages uses maximum available size-hint.
    170         QSize maxOfSizeHints;
    171         QList<UIWizardPage*> pages = findChildren<UIWizardPage*>();
    172         /* Search for the maximum available size-hint: */
    173         foreach (UIWizardPage *pPage, pages)
     559    // Due to async nature on macOS and X11, dialog doesn't come up to
     560    // time with layout update.  Waiting for layout update here:
     561    QCoreApplication::sendPostedEvents(0, QEvent::LayoutRequest);
     562#endif /* VBOX_WS_MAC || VBOX_WS_X11 */
     563
     564    /* Resize to minimum size-hint: */
     565    resize(minimumSizeHint());
     566}
     567
     568#ifdef VBOX_WS_MAC
     569void UINativeWizard::assignBackground()
     570{
     571    /* Load pixmap to icon first, this will gather HiDPI pixmaps as well: */
     572    const QIcon icon = UIIconPool::iconSet(m_strPixmapName);
     573
     574    /* Acquire pixmap of required size and scale (on basis of parent-widget's device pixel ratio): */
     575    const QSize standardSize(620, 440);
     576    const QPixmap pixmapOld = icon.pixmap(parentWidget()->windowHandle(), standardSize);
     577
     578    /* Assign background finally: */
     579    m_pLabelPixmap->setPixmap(pixmapOld);
     580    m_pLabelPixmap->resize(m_pLabelPixmap->minimumSizeHint());
     581}
     582
     583#else
     584
     585void UINativeWizard::assignWatermark()
     586{
     587    /* Load pixmap to icon first, this will gather HiDPI pixmaps as well: */
     588    const QIcon icon = UIIconPool::iconSet(m_strPixmapName);
     589
     590    /* Acquire pixmap of required size and scale (on basis of parent-widget's device pixel ratio): */
     591    const QSize standardSize(145, 290);
     592    const QPixmap pixmapOld = icon.pixmap(parentWidget()->windowHandle(), standardSize);
     593
     594    /* Convert watermark to image which allows to manage pixel data directly: */
     595    const QImage imageOld = pixmapOld.toImage();
     596    /* Use the right-top watermark pixel as frame color: */
     597    const QRgb rgbFrame = imageOld.pixel(imageOld.width() - 1, 0);
     598
     599    /* Upscale desired height up to pixmap device pixel ratio: */
     600    const int iDesiredHeight = m_pWidgetStack->minimumSizeHint().height() * pixmapOld.devicePixelRatio();
     601    /* Create final image on the basis of incoming, applying the rules: */
     602    QImage imageNew(imageOld.width(), qMax(imageOld.height(), iDesiredHeight), imageOld.format());
     603    for (int y = 0; y < imageNew.height(); ++y)
     604    {
     605        for (int x = 0; x < imageNew.width(); ++x)
    174606        {
    175             maxOfSizeHints.rwidth() = pPage->sizeHint().width() > maxOfSizeHints.width() ?
    176                                       pPage->sizeHint().width() : maxOfSizeHints.width();
    177             maxOfSizeHints.rheight() = pPage->sizeHint().height() > maxOfSizeHints.height() ?
    178                                        pPage->sizeHint().height() : maxOfSizeHints.height();
     607            /* Border rule: */
     608            if (x == imageNew.width() - 1)
     609                imageNew.setPixel(x, y, rgbFrame);
     610            /* Horizontal extension rule - use last used color: */
     611            else if (x >= imageOld.width() && y < imageOld.height())
     612                imageNew.setPixel(x, y, imageOld.pixel(imageOld.width() - 1, y));
     613            /* Vertical extension rule - use last used color: */
     614            else if (y >= imageOld.height() && x < imageOld.width())
     615                imageNew.setPixel(x, y, imageOld.pixel(x, imageOld.height() - 1));
     616            /* Common extension rule - use last used color: */
     617            else if (x >= imageOld.width() && y >= imageOld.height())
     618                imageNew.setPixel(x, y, imageOld.pixel(imageOld.width() - 1, imageOld.height() - 1));
     619            /* Else just copy color: */
     620            else
     621                imageNew.setPixel(x, y, imageOld.pixel(x, y));
    179622        }
    180         /* Feat corresponding height: */
    181         maxOfSizeHints.setWidth(qMax((int)(1.5 * maxOfSizeHints.height()), maxOfSizeHints.width()));
    182         /* Use that size-hint for all the pages: */
    183         foreach (UIWizardPage *pPage, pages)
    184             pPage->setMinimumSize(maxOfSizeHints);
    185 
    186         /* Relayout widgets: */
    187         QList<QLayout*> layouts = findChildren<QLayout*>();
    188         foreach(QLayout *pLayout, layouts)
    189             pLayout->activate();
    190 
    191         // WORKAROUND:
    192         // Unfortunately QWizard hides some of useful API in private part,
    193         // and also have few layouting bugs which could be easy fixed
    194         // by that API, so we will use QWizard::restart() method
    195         // to call the same functionality indirectly...
    196         // And now we call restart() after layout activation procedure!
    197         restart();
    198 
    199         /* Resize it to minimum size: */
    200         resize(QSize(0, 0));
    201     }
    202     else
    203     {
    204         /* Use some small (!) initial QIRichTextLabel width: */
    205         int iInitialLabelWidth = 200;
    206 
    207         /* Resize wizard according that initial width,
    208          * actually there could be other content
    209          * which wants to be wider than that initial width. */
    210         resizeAccordingLabelWidth(iInitialLabelWidth);
    211 
    212         /* Get some (first) of those pages: */
    213         QList<int> pids = pageIds();
    214         UIWizardPage *pPage = qobject_cast<UIWizardPage*>(page(pids.first()));
    215         /* Calculate actual label width: */
    216         int iPageWidth = pPage->minimumWidth();
    217         int iLeft, iTop, iRight, iBottom;
    218         pPage->layout()->getContentsMargins(&iLeft, &iTop, &iRight, &iBottom);
    219         int iCurrentLabelWidth = iPageWidth - iLeft - iRight;
    220         /* Calculate summary margin length,
    221          * including margins of the page and the wizard: */
    222         int iMarginsLength = width() - iCurrentLabelWidth;
    223 
    224         /* Get current wizard width and height: */
    225         int iCurrentWizardWidth = width();
    226         int iCurrentWizardHeight = height();
    227 #ifndef VBOX_WS_MAC
    228         /* Calculate metric and ratio: */
    229         const int iIconMetric = QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize);
    230         const double dRatio = (double)iIconMetric / 32;
    231         /* Load pixmap to icon first: */
    232         QIcon icon = UIIconPool::iconSet(m_strWatermarkName);
    233         QSize size = icon.availableSizes().value(0, QSize(145, 290));
    234         size *= dRatio;
    235         /* We should take into account watermark like its assigned already: */
    236         QPixmap watermarkPixmap(icon.pixmap(size));
    237         const int iWatermarkWidth = watermarkPixmap.width() * dRatio;
    238         iCurrentWizardWidth += iWatermarkWidth;
    239 #endif /* !VBOX_WS_MAC */
    240         /* Calculating nearest to 'golden ratio' label width: */
    241         int iGoldenRatioWidth = (int)qSqrt(ratio() * iCurrentWizardWidth * iCurrentWizardHeight);
    242         int iProposedLabelWidth = iGoldenRatioWidth - iMarginsLength;
    243 #ifndef VBOX_WS_MAC
    244         /* We should take into account watermark like its assigned already: */
    245         iProposedLabelWidth -= iWatermarkWidth;
    246 #endif /* !VBOX_WS_MAC */
    247 
    248         /* Choose maximum between current and proposed label width: */
    249         int iNewLabelWidth = qMax(iCurrentLabelWidth, iProposedLabelWidth);
    250 
    251         /* Finally resize wizard according new label width,
    252          * taking into account all the content and 'golden ratio' rule: */
    253         resizeAccordingLabelWidth(iNewLabelWidth);
    254     }
    255 
    256 #ifndef VBOX_WS_MAC
    257     /* Really assign watermark: */
    258     if (!m_strWatermarkName.isEmpty())
    259         assignWatermarkHelper();
    260 #endif
    261 }
    262 
    263 #ifndef VBOX_WS_MAC
    264 
    265 void UIWizard::assignWatermark(const QString &strWatermark)
    266 {
    267         m_strWatermarkName = strWatermark;
    268 }
    269 
    270 #else
    271 
    272 void UIWizard::assignBackground(const QString &strBackground)
    273 {
    274     setPixmap(QWizard::BackgroundPixmap, strBackground);
    275 }
    276 
    277 #endif
    278 
    279 void UIWizard::enableHelpButton(const QString &strHelpKeyword)
    280 {
    281     setOptions(options() | QWizard::HaveHelpButton);
    282     uiCommon().setHelpKeyword(this, strHelpKeyword);
    283     if (button(QWizard::HelpButton))
    284         button(QWizard::HelpButton)->setShortcut(QKeySequence::HelpContents);
    285     connect(this, &UIWizard::helpRequested, &msgCenter(), &UIMessageCenter::sltHandleHelpRequest);
    286 }
    287 
    288 void UIWizard::sltCurrentIdChanged(int iId)
    289 {
    290     /* Hide/show description button disabled by default: */
    291     bool fIsHideShowDescriptionButtonAvailable = false;
    292     /* Enable hide/show description button for 1st page: */
    293     if (iId == 0)
    294         fIsHideShowDescriptionButtonAvailable = true;
    295     /* But first-run wizard has no such button anyway: */
    296     if (m_enmType == WizardType_FirstRun)
    297         fIsHideShowDescriptionButtonAvailable = false;
    298     /* Set a flag for hide/show description button finally: */
    299     setOption(QWizard::HaveCustomButton1, fIsHideShowDescriptionButtonAvailable);
    300 }
    301 
    302 void UIWizard::sltCustomButtonClicked(int iId)
    303 {
    304     /* Handle 1st button: */
    305     if (iId == CustomButton1)
    306     {
    307         /* Cleanup: */
    308         cleanup();
    309 
    310         /* Toggle mode: */
    311         switch (m_enmMode)
    312         {
    313             case WizardMode_Basic:  m_enmMode = WizardMode_Expert; break;
    314             case WizardMode_Expert: m_enmMode = WizardMode_Basic;  break;
    315             default: AssertMsgFailed(("Invalid mode: %d", m_enmMode)); break;
    316         }
    317         /* Save mode: */
    318         gEDataManager->setModeForWizardType(m_enmType, m_enmMode);
    319 
    320         /* Prepare: */
    321         prepare();
    322     }
    323 }
    324 
    325 void UIWizard::retranslatePages()
    326 {
    327     /* Translate all the pages: */
    328     QList<int> ids = pageIds();
    329     for (int i = 0; i < ids.size(); ++i)
    330         qobject_cast<UIWizardPage*>(page(ids[i]))->retranslate();
    331 }
    332 
    333 void UIWizard::configurePage(UIWizardPage *pPage)
    334 {
    335     /* Page margins: */
    336     switch (wizardStyle())
    337     {
    338         case QWizard::ClassicStyle:
    339         {
    340             int iLeft, iTop, iRight, iBottom;
    341             pPage->layout()->getContentsMargins(&iLeft, &iTop, &iRight, &iBottom);
    342             pPage->layout()->setContentsMargins(iLeft, iTop, 0, 0);
    343             break;
    344         }
    345         default:
    346             break;
    347     }
    348 }
    349 
    350 void UIWizard::resizeAccordingLabelWidth(int iLabelsWidth)
    351 {
    352     // WORKAROUND:
    353     // Unfortunately QWizard hides some of useful API in private part,
    354     // and also have few layouting bugs which could be easy fixed
    355     // by that API, so we will use QWizard::restart() method
    356     // to call the same functionality indirectly...
    357     // Early call restart() which is usually goes on show()!
    358     restart();
    359 
    360     /* Update QIRichTextLabel(s) text-width(s): */
    361     QList<QIRichTextLabel*> labels = findChildren<QIRichTextLabel*>();
    362     foreach (QIRichTextLabel *pLabel, labels)
    363         pLabel->setMinimumTextWidth(iLabelsWidth);
    364 
    365     /* Now we have correct label size-hint(s) for all the pages.
    366      * We have to make sure all the pages uses maximum available size-hint. */
    367     QSize maxOfSizeHints;
    368     QList<UIWizardPage*> pages = findChildren<UIWizardPage*>();
    369     /* Search for the maximum available size-hint: */
    370     foreach (UIWizardPage *pPage, pages)
    371     {
    372         maxOfSizeHints.rwidth() = pPage->sizeHint().width() > maxOfSizeHints.width() ?
    373                                   pPage->sizeHint().width() : maxOfSizeHints.width();
    374         maxOfSizeHints.rheight() = pPage->sizeHint().height() > maxOfSizeHints.height() ?
    375                                    pPage->sizeHint().height() : maxOfSizeHints.height();
    376     }
    377     /* Use that size-hint for all the pages: */
    378     foreach (UIWizardPage *pPage, pages)
    379         pPage->setMinimumSize(maxOfSizeHints);
    380 
    381     /* Relayout widgets: */
    382     QList<QLayout*> layouts = findChildren<QLayout*>();
    383     foreach(QLayout *pLayout, layouts)
    384         pLayout->activate();
    385 
    386     // WORKAROUND:
    387     // Unfortunately QWizard hides some of useful API in private part,
    388     // and also have few layouting bugs which could be easy fixed
    389     // by that API, so we will use QWizard::restart() method
    390     // to call the same functionality indirectly...
    391     // And now we call restart() after layout activation procedure!
    392     restart();
    393 
    394     /* Resize it to minimum size: */
    395     resize(QSize(0, 0));
    396 }
    397 
    398 double UIWizard::ratio() const
    399 {
    400     /* Default value: */
    401     double dRatio = 1.6;
    402 
    403 #ifdef VBOX_WS_WIN
    404     switch (wizardStyle())
    405     {
    406         case QWizard::ClassicStyle:
    407         case QWizard::ModernStyle:
    408             // WORKAROUND:
    409             // There is a Qt bug about Windows7 do NOT match conditions for 'aero' wizard-style,
    410             // so its silently fallbacks to 'modern' one without any notification,
    411             // so QWizard::wizardStyle() returns QWizard::ModernStyle, while using aero, at least partially.
    412             if (QSysInfo::windowsVersion() != QSysInfo::WV_WINDOWS7)
    413             {
    414                 dRatio = 2;
    415                 break;
    416             }
    417         case QWizard::AeroStyle:
    418             dRatio = 2.2;
    419             break;
    420         default:
    421             break;
    422     }
    423 #endif /* VBOX_WS_WIN */
    424 
    425     switch (m_enmType)
    426     {
    427         case WizardType_CloneVM:
    428         case WizardType_ExportAppliance:
    429         case WizardType_ImportAppliance:
    430             dRatio -= 0.4;
    431             break;
    432         case WizardType_NewVD:
    433         case WizardType_CloneVD:
    434             dRatio += 0.1;
    435             break;
    436         case WizardType_FirstRun:
    437             dRatio += 0.3;
    438             break;
    439         default:
    440             break;
    441     }
    442 
    443     /* Return final result: */
    444     return dRatio;
    445 }
    446 
    447 #ifndef VBOX_WS_MAC
    448 int UIWizard::proposedWatermarkHeight()
    449 {
    450     /* We should calculate suitable height for watermark pixmap,
    451      * for that we have to take into account:
    452      * 1. wizard-layout top-margin (for modern style),
    453      * 2. wizard-header height,
    454      * 3. spacing between wizard-header and wizard-page,
    455      * 4. wizard-page height,
    456      * 5. wizard-layout bottom-margin (for modern style). */
    457 
    458     /* Get current application style: */
    459     QStyle *pStyle = QApplication::style();
    460 
    461     /* Acquire wizard-layout top-margin: */
    462     int iTopMargin = 0;
    463     if (m_enmMode == WizardMode_Basic)
    464     {
    465         if (wizardStyle() == QWizard::ModernStyle)
    466             iTopMargin = pStyle->pixelMetric(QStyle::PM_LayoutTopMargin);
    467     }
    468 
    469     /* Acquire wizard-header height: */
    470     int iTitleHeight = 0;
    471     if (m_enmMode == WizardMode_Basic)
    472     {
    473         /* We have no direct access to QWizardHeader inside QWizard private data...
    474          * From Qt sources it seems title font is hardcoded as current font point-size + 4: */
    475         QFont titleFont(QApplication::font());
    476         titleFont.setPointSize(titleFont.pointSize() + 4);
    477         QFontMetrics titleFontMetrics(titleFont);
    478         iTitleHeight = titleFontMetrics.height();
    479     }
    480 
    481     /* Acquire spacing between wizard-header and wizard-page: */
    482     int iMarginBetweenTitleAndPage = 0;
    483     if (m_enmMode == WizardMode_Basic)
    484     {
    485         /* We have no direct access to margin between QWizardHeader and wizard-pages...
    486          * From Qt sources it seems its hardcoded as just 7 pixels: */
    487         iMarginBetweenTitleAndPage = 7;
    488     }
    489 
    490     /* Acquire wizard-page height: */
    491     int iPageHeight = 0;
    492     if (page(0))
    493     {
    494         iPageHeight = page(0)->minimumSize().height();
    495     }
    496 
    497     /* Acquire wizard-layout bottom-margin: */
    498     int iBottomMargin = 0;
    499     if (wizardStyle() == QWizard::ModernStyle)
    500         iBottomMargin = pStyle->pixelMetric(QStyle::PM_LayoutBottomMargin);
    501 
    502     /* Finally, calculate summary height: */
    503     return iTopMargin + iTitleHeight + iMarginBetweenTitleAndPage + iPageHeight + iBottomMargin;
    504 }
    505 
    506 void UIWizard::assignWatermarkHelper()
    507 {
    508     /* Load pixmap to icon first: */
    509     QIcon icon = UIIconPool::iconSet(m_strWatermarkName);
    510     /* Create initial watermark pixmap.
    511      * For HiDPI support parent-widget's device pixel ratio is to be taken into account: */
    512     QPixmap pixWaterMark(  parentWidget()
    513                          ? icon.pixmap(parentWidget()->windowHandle(), QSize(145, 290))
    514                          : icon.pixmap(QSize(145, 290)));
    515     /* Convert watermark to image which
    516      * allows to manage pixel data directly: */
    517     QImage imgWatermark = pixWaterMark.toImage();
    518     /* Use the right-top watermark pixel as frame color: */
    519     QRgb rgbFrame = imgWatermark.pixel(imgWatermark.width() - 1, 0);
    520     /* Create final image on the basis of incoming, applying the rules: */
    521     QImage imgWatermarkNew(imgWatermark.width(), qMax(imgWatermark.height(), proposedWatermarkHeight()), imgWatermark.format());
    522     for (int y = 0; y < imgWatermarkNew.height(); ++y)
    523     {
    524         for (int x = 0; x < imgWatermarkNew.width(); ++x)
    525         {
    526             /* Border rule 1 - draw border for ClassicStyle */
    527             if (wizardStyle() == QWizard::ClassicStyle &&
    528                 (x == 0 || y == 0 || x == imgWatermarkNew.width() - 1 || y == imgWatermarkNew.height() - 1))
    529                 imgWatermarkNew.setPixel(x, y, rgbFrame);
    530             /* Border rule 2 - draw border for ModernStyle */
    531             else if (wizardStyle() == QWizard::ModernStyle && x == imgWatermarkNew.width() - 1)
    532                 imgWatermarkNew.setPixel(x, y, rgbFrame);
    533             /* Horizontal extension rule - use last used color */
    534             else if (x >= imgWatermark.width() && y < imgWatermark.height())
    535                 imgWatermarkNew.setPixel(x, y, imgWatermark.pixel(imgWatermark.width() - 1, y));
    536             /* Vertical extension rule - use last used color */
    537             else if (y >= imgWatermark.height() && x < imgWatermark.width())
    538                 imgWatermarkNew.setPixel(x, y, imgWatermark.pixel(x, imgWatermark.height() - 1));
    539             /* Common extension rule - use last used color */
    540             else if (x >= imgWatermark.width() && y >= imgWatermark.height())
    541                 imgWatermarkNew.setPixel(x, y, imgWatermark.pixel(imgWatermark.width() - 1, imgWatermark.height() - 1));
    542             /* Else just copy color */
    543             else
    544                 imgWatermarkNew.setPixel(x, y, imgWatermark.pixel(x, y));
    545         }
    546     }
    547     /* Convert processed image to pixmap and assign it to wizard's watermark. */
    548     QPixmap pixWatermarkNew = QPixmap::fromImage(imgWatermarkNew);
     623    }
     624
     625    /* Convert processed image to pixmap: */
     626    QPixmap pixmapNew = QPixmap::fromImage(imageNew);
    549627    /* For HiDPI support parent-widget's device pixel ratio is to be taken into account: */
    550628    const double dRatio = parentWidget()->windowHandle()->devicePixelRatio();
    551     pixWatermarkNew.setDevicePixelRatio(dRatio);
     629    pixmapNew.setDevicePixelRatio(dRatio);
    552630    /* Assign watermark finally: */
    553     setPixmap(QWizard::WatermarkPixmap, pixWatermarkNew);
     631    m_pLabelPixmap->setPixmap(pixmapNew);
    554632}
    555633#endif /* !VBOX_WS_MAC */
  • trunk/src/VBox/Frontends/VirtualBox/src/wizards/UINativeWizard.h

    r89882 r89960  
    11/* $Id$ */
    22/** @file
    3  * VBox Qt GUI - UIWizard class declaration.
     3 * VBox Qt GUI - UINativeWizard class declaration.
    44 */
    55
    66/*
    7  * Copyright (C) 2009-2020 Oracle Corporation
     7 * Copyright (C) 2009-2021 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    1616 */
    1717
    18 #ifndef FEQT_INCLUDED_SRC_wizards_UIWizard_h
    19 #define FEQT_INCLUDED_SRC_wizards_UIWizard_h
     18#ifndef FEQT_INCLUDED_SRC_wizards_UINativeWizard_h
     19#define FEQT_INCLUDED_SRC_wizards_UINativeWizard_h
    2020#ifndef RT_WITHOUT_PRAGMA_ONCE
    2121# pragma once
     
    2323
    2424/* Qt includes: */
     25#include <QMap>
    2526#include <QPointer>
    26 #include <QWizard>
    2727
    2828/* GUI includes: */
     
    3232
    3333/* Forward declarations: */
    34 class QShowEvent;
    35 class QString;
    36 class QWidget;
    37 class UIWizardPage;
     34class QLabel;
     35class QPushButton;
     36class QStackedWidget;
     37class UINativeWizardPage;
    3838
    39 /** QWizard extension with advanced functionality. */
    40 class SHARED_LIBRARY_STUFF UIWizard : public QIWithRetranslateUI<QWizard>
     39/** Native wizard buttons. */
     40enum WizardButtonType
     41{
     42    WizardButtonType_Invalid,
     43    WizardButtonType_Help,
     44    WizardButtonType_Expert,
     45    WizardButtonType_Back,
     46    WizardButtonType_Next,
     47    WizardButtonType_Cancel,
     48    WizardButtonType_Max,
     49};
     50Q_DECLARE_METATYPE(WizardButtonType);
     51
     52#ifdef VBOX_WS_MAC
     53/** QWidget-based QFrame analog with one particular purpose to
     54  * simulate macOS wizard frame without influencing palette hierarchy. */
     55class SHARED_LIBRARY_STUFF UIFrame : public QWidget
    4156{
    4257    Q_OBJECT;
     
    4459public:
    4560
    46     /** Returns wizard mode. */
    47     WizardMode mode() const { return m_enmMode; }
     61    /** Constructs UIFrame passing @a pParent to the base-class. */
     62    UIFrame(QWidget *pParent);
    4863
    49     /** Prepare all. */
    50     virtual void prepare();
     64protected:
     65
     66    /** Handles paint @a pEvent. */
     67    virtual void paintEvent(QPaintEvent *pEvent) /* final */;
     68};
     69#endif /* VBOX_WS_MAC */
     70
     71/** QDialog extension with advanced functionality emulating QWizard behavior. */
     72class SHARED_LIBRARY_STUFF UINativeWizard : public QIWithRetranslateUI<QDialog>
     73{
     74    Q_OBJECT;
     75
     76public slots:
     77
     78    /** Executes wizard in window modal mode.
     79      * @note You shouldn't have to override it! */
     80    virtual int exec() /* final */;
    5181
    5282protected:
    5383
    5484    /** Constructs wizard passing @a pParent to the base-class.
    55       * @param  enmType  Brings the wizard type.
    56       * @param  enmMode  Brings the wizard mode. */
    57     UIWizard(QWidget *pParent, WizardType enmType, WizardMode enmMode = WizardMode_Auto);
     85      * @param  enmType         Brings the wizard type.
     86      * @param  enmMode         Brings the wizard mode.
     87      * @param  strHelpHashtag  Brings the wizard help hashtag. */
     88    UINativeWizard(QWidget *pParent,
     89                   WizardType enmType,
     90                   WizardMode enmMode = WizardMode_Auto,
     91                   const QString &strHelpHashtag = QString());
     92
     93    /** Returns wizard type. */
     94    WizardType type() const { return m_enmType; }
     95    /** Returns wizard mode. */
     96    WizardMode mode() const { return m_enmMode; }
     97
     98    /** Returns wizard button of specified @a enmType. */
     99    QPushButton *wizardButton(const WizardButtonType &enmType) const;
     100    /** Defines @a strName for wizard button of specified @a enmType. */
     101    void setWizardButtonName(const WizardButtonType &enmType, const QString &strName);
     102
     103    /** Defines pixmap @a strName. */
     104    void setPixmapName(const QString &strName);
     105
     106    /** Appends wizard @a pPage.
     107      * @returns assigned page index. */
     108    int addPage(UINativeWizardPage *pPage);
     109    /** Populates pages.
     110      * @note In your subclasses you should add
     111      *       pages via addPage declared above. */
     112    virtual void populatePages() = 0;
    58113
    59114    /** Handles translation event. */
    60115    virtual void retranslateUi() /* override */;
    61116
    62     /** Handles show @a pEvent. */
    63     virtual void showEvent(QShowEvent *pEvent) /* override */;
     117private slots:
    64118
    65     /** Assigns @a pPage as a wizard page with certain @a iId. */
    66     void setPage(int iId, UIWizardPage *pPage);
    67     /** Removes all the pages. */
    68     void cleanup();
     119    /** Handles current-page change to page with @a iIndex. */
     120    void sltCurrentIndexChanged(int iIndex = -1);
     121    /** Handles page validity changes. */
     122    void sltCompleteChanged();
    69123
    70     /** Resizes wizard to golden ratio. */
    71     void resizeToGoldenRatio();
    72 
    73 #ifndef VBOX_WS_MAC
    74     /** Assigns @a strWaterMark. */
    75     void assignWatermark(const QString &strWaterMark);
    76 #else
    77     /** Assigns @a strBackground. */
    78     void assignBackground(const QString &strBackground);
    79 #endif
    80     /** Inserts the standard help button to the button box of the wizard. @param strHelpKeyword
    81       * is set as property. This is used for context sensitive help. */
    82     void enableHelpButton(const QString &strHelpKeyword);
    83 
    84 protected slots:
    85 
    86     /** Handles current-page change to page with @a iId. */
    87     virtual void sltCurrentIdChanged(int iId);
    88     /** Handles custome-button click for button with @a iId. */
    89     virtual void sltCustomButtonClicked(int iId);
     124    /** Toggles between basic and expert modes. */
     125    void sltExpert();
     126    /** Switches to previous page. */
     127    void sltPrevious();
     128    /** Switches to next page. */
     129    void sltNext();
    90130
    91131private:
     132
     133    /** Prepares all. */
     134    void prepare();
     135    /** Cleanups all. */
     136    void cleanup();
     137    /** Inits all. */
     138    void init();
    92139
    93140    /** Performs pages translation. */
    94141    void retranslatePages();
    95142
    96     /** Configures certain @a pPage. */
    97     void configurePage(UIWizardPage *pPage);
    98 
    99     /** Resizes wizard according certain @a iLabelWidth. */
    100     void resizeAccordingLabelWidth(int iLabelWidth);
    101 
    102     /** Returns ratio corresponding to current wizard type. */
    103     double ratio() const;
    104 
    105 #ifndef VBOX_WS_MAC
    106     /** Returns proposed watermark height. */
    107     int proposedWatermarkHeight();
    108     /** Assigns cached watermark. */
    109     void assignWatermarkHelper();
     143    /** Resizes wizard to golden ratio. */
     144    void resizeToGoldenRatio();
     145#ifdef VBOX_WS_MAC
     146    /** Assigns wizard background. */
     147    void assignBackground();
     148#else
     149    /** Assigns wizard watermark. */
     150    void assignWatermark();
    110151#endif
    111152
     
    114155    /** Holds the wizard mode. */
    115156    WizardMode  m_enmMode;
    116 #ifndef VBOX_WS_MAC
    117     /** Holds the watermark name. */
    118     QString     m_strWatermarkName;
     157    /** Holds the wizard help hashtag. */
     158    QString     m_strHelpHashtag;
     159    /** Holds the pixmap name. */
     160    QString     m_strPixmapName;
     161
     162    /** Holds the pixmap label instance. */
     163    QLabel                               *m_pLabelPixmap;
     164#ifdef VBOX_WS_MAC
     165    QLabel                               *m_pLabelPageTitle;
    119166#endif
     167    /** Holds the widget-stack instance. */
     168    QStackedWidget                       *m_pWidgetStack;
     169    /** Holds button instance map. */
     170    QMap<WizardButtonType, QPushButton*>  m_buttons;
    120171};
    121172
    122 /** Wizard interface safe-pointer. */
    123 typedef QPointer<UIWizard> UISafePointerWizard;
     173/** Native wizard interface pointer. */
     174typedef QPointer<UINativeWizard> UINativeWizardPointer;
    124175
    125 #endif /* !FEQT_INCLUDED_SRC_wizards_UIWizard_h */
     176#endif /* !FEQT_INCLUDED_SRC_wizards_UINativeWizard_h */
  • trunk/src/VBox/Frontends/VirtualBox/src/wizards/UINativeWizardPage.cpp

    r89882 r89960  
    11/* $Id$ */
    22/** @file
    3  * VBox Qt GUI - UIWizardPage class implementation.
     3 * VBox Qt GUI - UINativeWizardPage class implementation.
    44 */
    55
    66/*
    7  * Copyright (C) 2009-2020 Oracle Corporation
     7 * Copyright (C) 2009-2021 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    1616 */
    1717
    18 /* Qt includes: */
    19 #include <QAbstractButton>
    20 
    2118/* GUI includes: */
    22 #include "UICommon.h"
    23 #include "UIWizard.h"
    24 #include "UIWizardPage.h"
     19#include "UINativeWizard.h"
     20#include "UINativeWizardPage.h"
    2521
    2622
    27 /*********************************************************************************************************************************
    28 *   Class UIWizardPageBase implementation.                                                                                       *
    29 *********************************************************************************************************************************/
    30 
    31 UIWizard *UIWizardPageBase::wizardImp() const
    32 {
    33     /* Should be reimplemented in sub-class to enable access to wizard! */
    34     AssertMsgFailed(("UIWizardPageBase::wizardImp() should be reimplemented!"));
    35     return 0;
    36 }
    37 
    38 UIWizardPage *UIWizardPageBase::thisImp()
    39 {
    40     /* Should be reimplemented in sub-class to enable access to wizard page! */
    41     AssertMsgFailed(("UIWizardPageBase::thisImp() should be reimplemented!"));
    42     return 0;
    43 }
    44 
    45 QVariant UIWizardPageBase::fieldImp(const QString &) const
    46 {
    47     /* Should be reimplemented in sub-class to enable access to wizard field! */
    48     AssertMsgFailed(("UIWizardPageBase::fieldImp(const QString &) should be reimplemented!"));
    49     return QVariant();
    50 }
    51 
    52 
    53 /*********************************************************************************************************************************
    54 *   Class UIWizardPage implementation.                                                                                           *
    55 *********************************************************************************************************************************/
    56 
    57 UIWizardPage::UIWizardPage()
    58     : m_fReady(false)
     23UINativeWizardPage::UINativeWizardPage()
    5924{
    6025}
    6126
    62 void UIWizardPage::markReady()
     27void UINativeWizardPage::setTitle(const QString &strTitle)
    6328{
    64     m_fReady = true;
    65     QWizardPage::setTitle(m_strTitle);
     29    m_strTitle = strTitle;
    6630}
    6731
    68 void UIWizardPage::setTitle(const QString &strTitle)
     32QString UINativeWizardPage::title() const
    6933{
    70     m_strTitle = strTitle;
    71     if (m_fReady)
    72         QWizardPage::setTitle(m_strTitle);
     34    return m_strTitle;
    7335}
    7436
    75 UIWizard *UIWizardPage::wizard() const
     37UINativeWizard *UINativeWizardPage::wizard() const
    7638{
    77     return qobject_cast<UIWizard*>(QWizardPage::wizard());
     39    return   parentWidget() && parentWidget()->window()
     40           ? qobject_cast<UINativeWizard*>(parentWidget()->window())
     41           : 0;
    7842}
    79 
    80 void UIWizardPage::startProcessing()
    81 {
    82     if (isFinalPage())
    83         wizard()->button(QWizard::FinishButton)->setEnabled(false);
    84 }
    85 
    86 void UIWizardPage::endProcessing()
    87 {
    88     if (isFinalPage())
    89         wizard()->button(QWizard::FinishButton)->setEnabled(true);
    90 }
  • trunk/src/VBox/Frontends/VirtualBox/src/wizards/UINativeWizardPage.h

    r89882 r89960  
    11/* $Id$ */
    22/** @file
    3  * VBox Qt GUI - UIWizardPage class declaration.
     3 * VBox Qt GUI - UINativeWizardPage class declaration.
    44 */
    55
    66/*
    7  * Copyright (C) 2009-2020 Oracle Corporation
     7 * Copyright (C) 2009-2021 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    1616 */
    1717
    18 #ifndef FEQT_INCLUDED_SRC_wizards_UIWizardPage_h
    19 #define FEQT_INCLUDED_SRC_wizards_UIWizardPage_h
     18#ifndef FEQT_INCLUDED_SRC_wizards_UINativeWizardPage_h
     19#define FEQT_INCLUDED_SRC_wizards_UINativeWizardPage_h
    2020#ifndef RT_WITHOUT_PRAGMA_ONCE
    2121# pragma once
    2222#endif
    23 
    24 /* Qt includes: */
    25 #include <QVariant>
    26 #include <QWizardPage>
    2723
    2824/* GUI includes: */
     
    3127
    3228/* Forward declarations: */
    33 class UIWizard;
    34 class UIWizardPage;
     29class UINativeWizard;
    3530
    36 
    37 /** One of two interfaces for wizard page.
    38   * This is page-BASE providing access API for basic/expert pages. */
    39 class SHARED_LIBRARY_STUFF UIWizardPageBase
    40 {
    41 public:
    42 
    43     /** Destructs wizard page-base. */
    44     virtual ~UIWizardPageBase() { /* Makes MSC happy. */ }
    45 
    46 protected:
    47 
    48     /** Returns wizard this page-base belongs to. */
    49     virtual UIWizard *wizardImp() const;
    50 
    51     /** Returns wizard page this page-base belongs to. */
    52     virtual UIWizardPage *thisImp();
    53 
    54     /** Returns page field with certain @a strFieldName. */
    55     virtual QVariant fieldImp(const QString &strFieldName) const;
    56 };
    57 
    58 
    59 /** One of two interfaces for wizard page.
    60   * This is page-BODY based on QWizardPage with advanced functionality. */
    61 class SHARED_LIBRARY_STUFF UIWizardPage : public QIWithRetranslateUI<QWizardPage>
     31/** QWidget extension with advanced functionality emulating QWizardPage behavior. */
     32class SHARED_LIBRARY_STUFF UINativeWizardPage : public QIWithRetranslateUI<QWidget>
    6233{
    6334    Q_OBJECT;
     35
     36signals:
     37
     38    /** Notifies about page validity changes. */
     39    void completeChanged();
    6440
    6541public:
    6642
    6743    /** Constructs wizard page. */
    68     UIWizardPage();
     44    UINativeWizardPage();
    6945
    7046    /** Redirects the translation call to actual handler. */
    7147    void retranslate() { retranslateUi(); }
    7248
    73     /** Marks page ready. */
    74     void markReady();
    75 
    7649    /** Defines page @a strTitle. */
    7750    void setTitle(const QString &strTitle);
     51    /** Returns page title. */
     52    QString title() const;
     53
     54    /** Handles the page initialization. */
     55    virtual void initializePage() {}
     56    /** Tests the page for completeness, enables the Next button if Ok.
     57      * @returns whether all conditions to go to next page are satisfied. */
     58    virtual bool isComplete() const { return true; }
     59    /** Tests the page for validity, tranfers to the Next page is Ok.
     60      * @returns whether page state to go to next page is bearable. */
     61    virtual bool validatePage() { return true; }
    7862
    7963protected:
    8064
    8165    /** Returns wizard this page belongs to. */
    82     UIWizard *wizard() const;
     66    UINativeWizard *wizard() const;
    8367
    84     /** Starts page processing.  */
    85     void startProcessing();
    86     /** Ends page processing.  */
    87     void endProcessing();
    88 
    89     /** Holds whether page is ready. */
    90     bool m_fReady;
    9168    /** Holds the page title. */
    92     QString m_strTitle;
     69    QString  m_strTitle;
    9370};
    9471
    9572
    96 #endif /* !FEQT_INCLUDED_SRC_wizards_UIWizardPage_h */
     73#endif /* !FEQT_INCLUDED_SRC_wizards_UINativeWizardPage_h */
Note: See TracChangeset for help on using the changeset viewer.

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