Index: trunk/src/VBox/Frontends/VirtualBox/Makefile.kmk
===================================================================
--- trunk/src/VBox/Frontends/VirtualBox/Makefile.kmk	(revision 89959)
+++ trunk/src/VBox/Frontends/VirtualBox/Makefile.kmk	(revision 89960)
@@ -903,4 +903,6 @@
 	src/widgets/UIToolBox.h \
 	src/widgets/UIWarningPane.h \
+	src/wizards/UINativeWizard.h \
+	src/wizards/UINativeWizardPage.h \
 	src/wizards/UIWizard.h \
 	src/wizards/UIWizardPage.h \
@@ -1445,4 +1447,6 @@
 	src/widgets/UIToolBox.cpp \
 	src/widgets/UIWarningPane.cpp \
+	src/wizards/UINativeWizard.cpp \
+	src/wizards/UINativeWizardPage.cpp \
 	src/wizards/UIWizard.cpp \
 	src/wizards/UIWizardPage.cpp \
Index: trunk/src/VBox/Frontends/VirtualBox/src/globals/QIWithRetranslateUI.h
===================================================================
--- trunk/src/VBox/Frontends/VirtualBox/src/globals/QIWithRetranslateUI.h	(revision 89959)
+++ trunk/src/VBox/Frontends/VirtualBox/src/globals/QIWithRetranslateUI.h	(revision 89960)
@@ -24,4 +24,5 @@
 /* Qt includes: */
 #include <QApplication>
+#include <QDialog>
 #include <QEvent>
 #include <QGraphicsWidget>
@@ -67,5 +68,5 @@
 };
 
-/** Explicit QIWithRetranslateUI instantiation for QWidget class.
+/** Explicit QIWithRetranslateUI instantiation for QWidget & QDialog classes.
   * @note  On Windows it's important that all template cases are instantiated just once across
   *        the linking space. In case we have particular template case instantiated from both
@@ -75,4 +76,5 @@
   *        to library because latter can have lack of required instantiations (current case). */
 template class SHARED_LIBRARY_STUFF QIWithRetranslateUI<QWidget>;
+template class SHARED_LIBRARY_STUFF QIWithRetranslateUI<QDialog>;
 
 
Index: trunk/src/VBox/Frontends/VirtualBox/src/wizards/UINativeWizard.cpp
===================================================================
--- trunk/src/VBox/Frontends/VirtualBox/src/wizards/UINativeWizard.cpp	(revision 89960)
+++ trunk/src/VBox/Frontends/VirtualBox/src/wizards/UINativeWizard.cpp	(revision 89960)
@@ -0,0 +1,633 @@
+/* $Id$ */
+/** @file
+ * VBox Qt GUI - UINativeWizard class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2021 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/* Qt includes: */
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QPainter>
+#include <QPushButton>
+#include <QStackedWidget>
+#include <QStyle>
+#include <QVBoxLayout>
+#include <QWindow>
+
+/* GUI includes: */
+#include "QIRichTextLabel.h"
+#include "UICommon.h"
+#include "UIDesktopWidgetWatchdog.h"
+#include "UIExtraDataManager.h"
+#include "UIIconPool.h"
+#include "UIMessageCenter.h"
+#include "UINativeWizard.h"
+#include "UINativeWizardPage.h"
+
+
+#ifdef VBOX_WS_MAC
+UIFrame::UIFrame(QWidget *pParent)
+    : QWidget(pParent)
+{
+}
+
+void UIFrame::paintEvent(QPaintEvent *pEvent)
+{
+    /* Prepare painter: */
+    QPainter painter(this);
+
+    /* Limit painting with incoming rectangle: */
+    painter.setClipRect(pEvent->rect());
+
+    /* Check whether we should use Active or Inactive palette: */
+    const bool fActive = parentWidget() && parentWidget()->isActiveWindow();
+
+    /* Paint background: */
+    QColor backgroundColor = QGuiApplication::palette().color(fActive ? QPalette::Active : QPalette::Inactive, QPalette::Window);
+    backgroundColor.setAlpha(100);
+    painter.setPen(backgroundColor);
+    painter.setBrush(backgroundColor);
+    painter.drawRect(rect());
+
+    /* Paint borders: */
+    painter.setPen(QGuiApplication::palette().color(fActive ? QPalette::Active : QPalette::Inactive, QPalette::Window).darker(130));
+    QLine line1(0,                  0,                      rect().width() - 1, 0);
+    QLine line2(rect().width() - 1, 0,                      rect().width() - 1, rect().height() - 1);
+    QLine line3(rect().width() - 1, rect().height() - 1, 0,                     rect().height() - 1);
+    QLine line4(0,                  rect().height() - 1, 0,                     0);
+    painter.drawLine(line1);
+    painter.drawLine(line2);
+    painter.drawLine(line3);
+    painter.drawLine(line4);
+}
+#endif /* VBOX_WS_MAC */
+
+
+int UINativeWizard::exec()
+{
+    /* Init wizard: */
+    init();
+
+    /* Call to base-class: */
+    return QIWithRetranslateUI<QDialog>::exec();
+}
+
+UINativeWizard::UINativeWizard(QWidget *pParent,
+                               WizardType enmType,
+                               WizardMode enmMode /* = WizardMode_Auto */,
+                               const QString &strHelpHashtag /* = QString() */)
+    : QIWithRetranslateUI<QDialog>(pParent)
+    , m_enmType(enmType)
+    , m_enmMode(enmMode == WizardMode_Auto ? gEDataManager->modeForWizardType(m_enmType) : enmMode)
+    , m_strHelpHashtag(strHelpHashtag)
+    , m_pLabelPixmap(0)
+#ifdef VBOX_WS_MAC
+    , m_pLabelPageTitle(0)
+#endif
+    , m_pWidgetStack(0)
+{
+    prepare();
+}
+
+QPushButton *UINativeWizard::wizardButton(const WizardButtonType &enmType) const
+{
+    return m_buttons.value(enmType);
+}
+
+void UINativeWizard::setPixmapName(const QString &strName)
+{
+    m_strPixmapName = strName;
+}
+
+int UINativeWizard::addPage(UINativeWizardPage *pPage)
+{
+    /* Adjust page layout: */
+    const int iL = m_enmMode == WizardMode_Expert
+                 ? qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin)
+                 : 0;
+    const int iT = m_enmMode == WizardMode_Expert
+                 ? qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin)
+                 : 0;
+    const int iR = qApp->style()->pixelMetric(QStyle::PM_LayoutRightMargin);
+    const int iB = qApp->style()->pixelMetric(QStyle::PM_LayoutBottomMargin);
+    pPage->layout()->setContentsMargins(iL, iT, iR, iB);
+
+    /* Add page to wizard's stack: */
+    m_pWidgetStack->blockSignals(true);
+    const int iIndex = m_pWidgetStack->addWidget(pPage);
+    m_pWidgetStack->blockSignals(false);
+
+    /* Make sure wizard is aware of page validity changes: */
+    connect(pPage, &UINativeWizardPage::completeChanged,
+            this, &UINativeWizard::sltCompleteChanged);
+
+    /* Returns added page index: */
+    return iIndex;
+}
+
+void UINativeWizard::retranslateUi()
+{
+    /* Translate Help button: */
+    QPushButton *pButtonHelp = wizardButton(WizardButtonType_Help);
+    AssertMsgReturnVoid(pButtonHelp, ("No Help wizard button found!\n"));
+    pButtonHelp->setText(tr("&Help"));
+    pButtonHelp->setToolTip(tr("Open corresponding Help topic."));
+
+    /* Translate basic/expert button: */
+    QPushButton *pButtonExpert = wizardButton(WizardButtonType_Expert);
+    AssertMsgReturnVoid(pButtonExpert, ("No Expert wizard button found!\n"));
+    switch (m_enmMode)
+    {
+        case WizardMode_Basic:
+            pButtonExpert->setText(tr("&Expert Mode"));
+            pButtonExpert->setToolTip(tr("Switch to <nobr><b>Expert Mode</b></nobr>, "
+                                         "a one-page dialog for experienced users."));
+            break;
+        case WizardMode_Expert:
+            pButtonExpert->setText(tr("&Guided Mode"));
+            pButtonExpert->setToolTip(tr("Switch to <nobr><b>Guided Mode</b></nobr>, "
+                                         "a step-by-step dialog with detailed explanations."));
+            break;
+        default:
+            AssertMsgFailed(("Invalid wizard mode: %d", m_enmMode));
+            break;
+    }
+
+    /* Translate Back button: */
+    QPushButton *pButtonBack = wizardButton(WizardButtonType_Back);
+    AssertMsgReturnVoid(pButtonBack, ("No Back wizard button found!\n"));
+    pButtonBack->setText(tr("&Back"));
+    pButtonBack->setToolTip(tr("Go to previous wizard page."));
+
+    /* Translate Next button: */
+    QPushButton *pButtonNext = wizardButton(WizardButtonType_Next);
+    AssertMsgReturnVoid(pButtonNext, ("No Next wizard button found!\n"));
+    if (m_pWidgetStack && m_pWidgetStack->currentIndex() < m_pWidgetStack->count() - 1)
+    {
+        pButtonNext->setText(tr("&Next"));
+        pButtonNext->setToolTip(tr("Go to next wizard page."));
+    }
+    else
+    {
+        pButtonNext->setText(tr("&Finish"));
+        pButtonNext->setToolTip(tr("Commit all wizard data."));
+    }
+
+    /* Translate Cancel button: */
+    QPushButton *pButtonCancel = wizardButton(WizardButtonType_Cancel);
+    AssertMsgReturnVoid(pButtonCancel, ("No Cancel wizard button found!\n"));
+    pButtonCancel->setText(tr("&Cancel"));
+    pButtonCancel->setToolTip(tr("Cancel wizard execution."));
+}
+
+void UINativeWizard::sltCurrentIndexChanged(int iIndex /* = -1 */)
+{
+    /* Update translation: */
+    retranslateUi();
+
+    /* Sanity check: */
+    AssertPtrReturnVoid(m_pWidgetStack);
+
+    /* -1 means current one page: */
+    if (iIndex == -1)
+        iIndex = m_pWidgetStack->currentIndex();
+
+    /* Hide/show Expert button (hidden by default): */
+    bool fIsExpertButtonAvailable = false;
+    /* Show Expert button for 1st page: */
+    if (iIndex == 0)
+        fIsExpertButtonAvailable = true;
+    /* But first-run wizard has no such button anyway: */
+    if (m_enmType == WizardType_FirstRun)
+        fIsExpertButtonAvailable = false;
+    /* Hide/show Expert button finally: */
+    QPushButton *pButtonExpert = wizardButton(WizardButtonType_Expert);
+    AssertMsgReturnVoid(pButtonExpert, ("No Expert wizard button found!\n"));
+    pButtonExpert->setVisible(fIsExpertButtonAvailable);
+
+    /* Disable/enable Back button: */
+    QPushButton *pButtonBack = wizardButton(WizardButtonType_Back);
+    AssertMsgReturnVoid(pButtonBack, ("No Back wizard button found!\n"));
+    pButtonBack->setEnabled(iIndex > 0);
+
+    /* Initialize corresponding page: */
+    UINativeWizardPage *pPage = qobject_cast<UINativeWizardPage*>(m_pWidgetStack->widget(iIndex));
+    AssertPtrReturnVoid(pPage);
+#ifdef VBOX_WS_MAC
+    m_pLabelPageTitle->setText(pPage->title());
+#endif
+    pPage->initializePage();
+}
+
+void UINativeWizard::sltCompleteChanged()
+{
+    /* Make sure sender is current widget: */
+    QWidget *pSender = qobject_cast<QWidget*>(sender());
+    AssertReturnVoid(m_pWidgetStack->currentWidget() == pSender);
+
+    /* Allow Next button only if current page is complete: */
+    UINativeWizardPage *pPage = qobject_cast<UINativeWizardPage*>(pSender);
+    QPushButton *pButtonNext = wizardButton(WizardButtonType_Next);
+    AssertMsgReturnVoid(pButtonNext, ("No Next wizard button found!\n"));
+    pButtonNext->setEnabled(pPage->isComplete());
+}
+
+void UINativeWizard::sltExpert()
+{
+    /* Toggle mode: */
+    switch (m_enmMode)
+    {
+        case WizardMode_Basic:  m_enmMode = WizardMode_Expert; break;
+        case WizardMode_Expert: m_enmMode = WizardMode_Basic;  break;
+        default: AssertMsgFailed(("Invalid mode: %d", m_enmMode)); break;
+    }
+    gEDataManager->setModeForWizardType(m_enmType, m_enmMode);
+
+    /* Cleanup/init again: */
+    cleanup();
+    init();
+}
+
+void UINativeWizard::sltPrevious()
+{
+    /* For all the pages besides the 1st one we going backward: */
+    AssertReturnVoid(m_pWidgetStack->currentIndex() > 0);
+    m_pWidgetStack->setCurrentIndex(m_pWidgetStack->currentIndex() - 1);
+}
+
+void UINativeWizard::sltNext()
+{
+    /* Look for Next button: */
+    QPushButton *pButtonNext = wizardButton(WizardButtonType_Next);
+    AssertMsgReturnVoid(pButtonNext, ("No Next wizard button found!\n"));
+
+    /* Validate page before going forward: */
+    AssertReturnVoid(m_pWidgetStack->currentIndex() < m_pWidgetStack->count());
+    UINativeWizardPage *pPage = qobject_cast<UINativeWizardPage*>(m_pWidgetStack->currentWidget());
+    AssertPtrReturnVoid(pPage);
+    pButtonNext->setEnabled(false);
+    const bool fIsPageValid = pPage->validatePage();
+    pButtonNext->setEnabled(true);
+    if (!fIsPageValid)
+        return;
+
+    /* For all the pages besides the last one we going forward: */
+    if (m_pWidgetStack->currentIndex() < m_pWidgetStack->count() - 1)
+        m_pWidgetStack->setCurrentIndex(m_pWidgetStack->currentIndex() + 1);
+    /* For last one we just accept the wizard: */
+    else
+        accept();
+}
+
+void UINativeWizard::prepare()
+{
+    /* Prepare main layout: */
+    QVBoxLayout *pLayoutMain = new QVBoxLayout(this);
+    if (pLayoutMain)
+    {
+        /* No need for margins and spacings between sub-layouts: */
+        pLayoutMain->setContentsMargins(0, 0, 0, 0);
+        pLayoutMain->setSpacing(0);
+
+        /* Prepare upper layout: */
+        QHBoxLayout *pLayoutUpper = new QHBoxLayout;
+        if (pLayoutUpper)
+        {
+#ifdef VBOX_WS_MAC
+            /* No need for bottom margin on macOS, reseting others to default: */
+            const int iL = qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin);
+            const int iT = qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin);
+            const int iR = qApp->style()->pixelMetric(QStyle::PM_LayoutRightMargin);
+            pLayoutUpper->setContentsMargins(iL, iT, iR, 0);
+#endif /* VBOX_WS_MAC */
+            /* Reset spacing to default, it was flawed by parent inheritance: */
+            const int iSpacing = qApp->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing);
+            pLayoutUpper->setSpacing(iSpacing);
+
+            /* Prepare pixmap label: */
+            m_pLabelPixmap = new QLabel(this);
+            if (m_pLabelPixmap)
+            {
+                m_pLabelPixmap->setAlignment(Qt::AlignTop);
+#ifdef VBOX_WS_MAC
+                /* On macOS this label contains background, which isn't a part of layout, moving manually: */
+                m_pLabelPixmap->move(0, 0);
+                /* Spacer to make look&feel native on macOS: */
+                QSpacerItem *pSpacer = new QSpacerItem(200, 0, QSizePolicy::Fixed, QSizePolicy::Minimum);
+                pLayoutUpper->addItem(pSpacer);
+#else /* !VBOX_WS_MAC */
+                /* Just add label into layout on other platforms: */
+                pLayoutUpper->addWidget(m_pLabelPixmap);
+#endif /* !VBOX_WS_MAC */
+            }
+
+#ifdef VBOX_WS_MAC
+            /* Prepare right layout on macOS for nativity purposes: */
+            QVBoxLayout *pLayoutRight = new QVBoxLayout;
+            if (pLayoutRight)
+            {
+                /* Prepare page title label: */
+                m_pLabelPageTitle = new QLabel(this);
+                if (m_pLabelPageTitle)
+                {
+                    /* Title should have big/fat font: */
+                    QFont labelFont = m_pLabelPageTitle->font();
+                    labelFont.setBold(true);
+                    labelFont.setPointSize(labelFont.pointSize() + 4);
+                    m_pLabelPageTitle->setFont(labelFont);
+
+                    pLayoutRight->addWidget(m_pLabelPageTitle);
+                }
+
+                /* Prepare frame around widget-stack on macOS for nativity purposes: */
+                UIFrame *pFrame = new UIFrame(this);
+                if (pFrame)
+                {
+                    /* Prepare frame layout: */
+                    QVBoxLayout *pLayoutFrame = new QVBoxLayout(pFrame);
+                    if (pLayoutFrame)
+                    {
+                        /* Prepare widget-stack: */
+                        m_pWidgetStack = new QStackedWidget(pFrame);
+                        if (m_pWidgetStack)
+                        {
+                            connect(m_pWidgetStack, &QStackedWidget::currentChanged, this, &UINativeWizard::sltCurrentIndexChanged);
+                            pLayoutFrame->addWidget(m_pWidgetStack);
+                        }
+                    }
+
+                    /* Add to layout: */
+                    pLayoutRight->addWidget(pFrame);
+                }
+
+                /* Add to layout: */
+                pLayoutUpper->addLayout(pLayoutRight);
+            }
+#else /* !VBOX_WS_MAC */
+            /* Prepare widget-stack directly on other platforms: */
+            m_pWidgetStack = new QStackedWidget(this);
+            if (m_pWidgetStack)
+            {
+                connect(m_pWidgetStack, &QStackedWidget::currentChanged, this, &UINativeWizard::sltCurrentIndexChanged);
+                pLayoutUpper->addWidget(m_pWidgetStack);
+            }
+#endif /* !VBOX_WS_MAC */
+
+            /* Add to layout: */
+            pLayoutMain->addLayout(pLayoutUpper);
+        }
+
+        /* Prepare bottom widget: */
+        QWidget *pWidgetBottom = new QWidget(this);
+        if (pWidgetBottom)
+        {
+#ifndef VBOX_WS_MAC
+            /* Adjust palette a bit on Windows/X11 for native purposes: */
+            pWidgetBottom->setAutoFillBackground(true);
+            QPalette pal = QGuiApplication::palette();
+            pal.setColor(QPalette::Active, QPalette::Window, pal.color(QPalette::Active, QPalette::Window).darker(110));
+            pal.setColor(QPalette::Inactive, QPalette::Window, pal.color(QPalette::Inactive, QPalette::Window).darker(110));
+            pWidgetBottom->setPalette(pal);
+#endif /* !VBOX_WS_MAC */
+
+            /* Prepare bottom layout: */
+            QHBoxLayout *pLayoutBottom = new QHBoxLayout(pWidgetBottom);
+            if (pLayoutBottom)
+            {
+                /* Reset margins to default, they were flawed by parent inheritance: */
+                const int iL = qApp->style()->pixelMetric(QStyle::PM_LayoutLeftMargin);
+                const int iT = qApp->style()->pixelMetric(QStyle::PM_LayoutTopMargin);
+                const int iR = qApp->style()->pixelMetric(QStyle::PM_LayoutRightMargin);
+                const int iB = qApp->style()->pixelMetric(QStyle::PM_LayoutBottomMargin);
+                pLayoutBottom->setContentsMargins(iL, iT, iR, iB);
+
+                // WORKAROUND:
+                // Prepare dialog button-box? Huh, no .. QWizard has different opinion.
+                // So we are hardcoding order, same on all platforms, which is the case.
+                for (int i = WizardButtonType_Invalid + 1; i < WizardButtonType_Max; ++i)
+                {
+                    const WizardButtonType enmType = (WizardButtonType)i;
+                    m_buttons[enmType] = new QPushButton(pWidgetBottom);
+                    QPushButton *pButton = wizardButton(enmType);
+                    if (pButton)
+                        pLayoutBottom->addWidget(pButton);
+                    if (enmType == WizardButtonType_Help)
+                        pLayoutBottom->addStretch(1);
+                    if (   pButton
+                        && enmType == WizardButtonType_Next)
+                        pButton->setDefault(true);
+                }
+                /* Connect buttons: */
+                connect(wizardButton(WizardButtonType_Help), &QPushButton::pressed,
+                        &(msgCenter()), &UIMessageCenter::sltHandleHelpRequest);
+                wizardButton(WizardButtonType_Help)->setShortcut(QKeySequence::HelpContents);
+                uiCommon().setHelpKeyword(wizardButton(WizardButtonType_Help), m_strHelpHashtag);
+                connect(wizardButton(WizardButtonType_Expert), &QPushButton::pressed,
+                        this, &UINativeWizard::sltExpert);
+                connect(wizardButton(WizardButtonType_Back), &QPushButton::pressed,
+                        this, &UINativeWizard::sltPrevious);
+                connect(wizardButton(WizardButtonType_Next), &QPushButton::pressed,
+                        this, &UINativeWizard::sltNext);
+                connect(wizardButton(WizardButtonType_Cancel), &QPushButton::pressed,
+                        this, &UINativeWizard::reject);
+            }
+
+            /* Add to layout: */
+            pLayoutMain->addWidget(pWidgetBottom);
+        }
+    }
+}
+
+void UINativeWizard::cleanup()
+{
+    /* Remove all the pages: */
+    m_pWidgetStack->blockSignals(true);
+    while (m_pWidgetStack->count() > 0)
+    {
+        QWidget *pLastWidget = m_pWidgetStack->widget(m_pWidgetStack->count() - 1);
+        m_pWidgetStack->removeWidget(pLastWidget);
+        delete pLastWidget;
+    }
+    m_pWidgetStack->blockSignals(false);
+}
+
+void UINativeWizard::init()
+{
+    /* Populate pages: */
+    populatePages();
+
+    /* Translate wizard: */
+    retranslateUi();
+    /* Translate wizard pages: */
+    retranslatePages();
+
+    /* Resize wizard to 'golden ratio': */
+    resizeToGoldenRatio();
+
+    /* Make sure current page initialized: */
+    sltCurrentIndexChanged();
+}
+
+void UINativeWizard::retranslatePages()
+{
+    /* Translate all the pages: */
+    for (int i = 0; i < m_pWidgetStack->count(); ++i)
+        qobject_cast<UINativeWizardPage*>(m_pWidgetStack->widget(i))->retranslate();
+}
+
+void UINativeWizard::resizeToGoldenRatio()
+{
+#ifdef VBOX_WS_MAC
+    /* Hide/show title label on macOS only, on Windows/X11 there is no title label. */
+    m_pLabelPageTitle->setVisible(m_enmMode == WizardMode_Basic);
+#else
+    /* Hide/show pixmap label on Windows/X11 only, on macOS it's in the background: */
+    m_pLabelPixmap->setVisible(!m_strPixmapName.isEmpty() && m_enmMode == WizardMode_Basic);
+#endif /* !VBOX_WS_MAC */
+
+    /* For wizard in Basic mode: */
+    if (m_enmMode == WizardMode_Basic)
+    {
+        /* Temporary hide all the QIRichTextLabel(s) to exclude
+         * influence onto m_pWidgetStack minimum size-hint below: */
+        foreach (QIRichTextLabel *pLabel, findChildren<QIRichTextLabel*>())
+            pLabel->hide();
+        /* Gather suitable dimensions: */
+        const int iStepWidth = 100;
+        const int iMinWidth = qMax(100, m_pWidgetStack->minimumSizeHint().width());
+        const int iMaxWidth = qMax(iMinWidth, gpDesktop->availableGeometry(this).width() * 3 / 4);
+        /* Show all the QIRichTextLabel(s) again, they were hidden above: */
+        foreach (QIRichTextLabel *pLabel, findChildren<QIRichTextLabel*>())
+            pLabel->show();
+        /* Now look for a golden ratio: */
+        int iCurrentWidth = iMinWidth;
+        do
+        {
+            /* Assign current QIRichTextLabel(s) width: */
+            foreach (QIRichTextLabel *pLabel, findChildren<QIRichTextLabel*>())
+                pLabel->setMinimumTextWidth(iCurrentWidth);
+
+            /* Calculate current ratio: */
+            const QSize msh = m_pWidgetStack->minimumSizeHint();
+            int iWidth = msh.width();
+            int iHeight = msh.height();
+#ifndef VBOX_WS_MAC
+            /* Advance width for standard watermark width: */
+            if (!m_strPixmapName.isEmpty())
+                iWidth += 145;
+#endif /* !VBOX_WS_MAC */
+            const double dRatio = (double)iWidth / iHeight;
+            if (dRatio > 1.6)
+                break;
+
+            /* Advance current width: */
+            iCurrentWidth += iStepWidth;
+        }
+        while (iCurrentWidth < iMaxWidth);
+    }
+
+#ifdef VBOX_WS_MAC
+    /* Assign background finally: */
+    if (!m_strPixmapName.isEmpty())
+        assignBackground();
+#else
+    /* Assign watermark finally: */
+    if (   !m_strPixmapName.isEmpty()
+        && m_enmMode == WizardMode_Basic)
+        assignWatermark();
+#endif /* !VBOX_WS_MAC */
+
+    /* Make sure layouts are freshly updated & activated: */
+    foreach (QLayout *pLayout, findChildren<QLayout*>())
+    {
+        pLayout->update();
+        pLayout->activate();
+    }
+#if defined(VBOX_WS_MAC) || defined(VBOX_WS_X11)
+    // WORKAROUND:
+    // Due to async nature on macOS and X11, dialog doesn't come up to
+    // time with layout update.  Waiting for layout update here:
+    QCoreApplication::sendPostedEvents(0, QEvent::LayoutRequest);
+#endif /* VBOX_WS_MAC || VBOX_WS_X11 */
+
+    /* Resize to minimum size-hint: */
+    resize(minimumSizeHint());
+}
+
+#ifdef VBOX_WS_MAC
+void UINativeWizard::assignBackground()
+{
+    /* Load pixmap to icon first, this will gather HiDPI pixmaps as well: */
+    const QIcon icon = UIIconPool::iconSet(m_strPixmapName);
+
+    /* Acquire pixmap of required size and scale (on basis of parent-widget's device pixel ratio): */
+    const QSize standardSize(620, 440);
+    const QPixmap pixmapOld = icon.pixmap(parentWidget()->windowHandle(), standardSize);
+
+    /* Assign background finally: */
+    m_pLabelPixmap->setPixmap(pixmapOld);
+    m_pLabelPixmap->resize(m_pLabelPixmap->minimumSizeHint());
+}
+
+#else
+
+void UINativeWizard::assignWatermark()
+{
+    /* Load pixmap to icon first, this will gather HiDPI pixmaps as well: */
+    const QIcon icon = UIIconPool::iconSet(m_strPixmapName);
+
+    /* Acquire pixmap of required size and scale (on basis of parent-widget's device pixel ratio): */
+    const QSize standardSize(145, 290);
+    const QPixmap pixmapOld = icon.pixmap(parentWidget()->windowHandle(), standardSize);
+
+    /* Convert watermark to image which allows to manage pixel data directly: */
+    const QImage imageOld = pixmapOld.toImage();
+    /* Use the right-top watermark pixel as frame color: */
+    const QRgb rgbFrame = imageOld.pixel(imageOld.width() - 1, 0);
+
+    /* Upscale desired height up to pixmap device pixel ratio: */
+    const int iDesiredHeight = m_pWidgetStack->minimumSizeHint().height() * pixmapOld.devicePixelRatio();
+    /* Create final image on the basis of incoming, applying the rules: */
+    QImage imageNew(imageOld.width(), qMax(imageOld.height(), iDesiredHeight), imageOld.format());
+    for (int y = 0; y < imageNew.height(); ++y)
+    {
+        for (int x = 0; x < imageNew.width(); ++x)
+        {
+            /* Border rule: */
+            if (x == imageNew.width() - 1)
+                imageNew.setPixel(x, y, rgbFrame);
+            /* Horizontal extension rule - use last used color: */
+            else if (x >= imageOld.width() && y < imageOld.height())
+                imageNew.setPixel(x, y, imageOld.pixel(imageOld.width() - 1, y));
+            /* Vertical extension rule - use last used color: */
+            else if (y >= imageOld.height() && x < imageOld.width())
+                imageNew.setPixel(x, y, imageOld.pixel(x, imageOld.height() - 1));
+            /* Common extension rule - use last used color: */
+            else if (x >= imageOld.width() && y >= imageOld.height())
+                imageNew.setPixel(x, y, imageOld.pixel(imageOld.width() - 1, imageOld.height() - 1));
+            /* Else just copy color: */
+            else
+                imageNew.setPixel(x, y, imageOld.pixel(x, y));
+        }
+    }
+
+    /* Convert processed image to pixmap: */
+    QPixmap pixmapNew = QPixmap::fromImage(imageNew);
+    /* For HiDPI support parent-widget's device pixel ratio is to be taken into account: */
+    const double dRatio = parentWidget()->windowHandle()->devicePixelRatio();
+    pixmapNew.setDevicePixelRatio(dRatio);
+    /* Assign watermark finally: */
+    m_pLabelPixmap->setPixmap(pixmapNew);
+}
+#endif /* !VBOX_WS_MAC */
Index: trunk/src/VBox/Frontends/VirtualBox/src/wizards/UINativeWizard.h
===================================================================
--- trunk/src/VBox/Frontends/VirtualBox/src/wizards/UINativeWizard.h	(revision 89960)
+++ trunk/src/VBox/Frontends/VirtualBox/src/wizards/UINativeWizard.h	(revision 89960)
@@ -0,0 +1,176 @@
+/* $Id$ */
+/** @file
+ * VBox Qt GUI - UINativeWizard class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2021 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_UINativeWizard_h
+#define FEQT_INCLUDED_SRC_wizards_UINativeWizard_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Qt includes: */
+#include <QMap>
+#include <QPointer>
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UIExtraDataDefs.h"
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class QLabel;
+class QPushButton;
+class QStackedWidget;
+class UINativeWizardPage;
+
+/** Native wizard buttons. */
+enum WizardButtonType
+{
+    WizardButtonType_Invalid,
+    WizardButtonType_Help,
+    WizardButtonType_Expert,
+    WizardButtonType_Back,
+    WizardButtonType_Next,
+    WizardButtonType_Cancel,
+    WizardButtonType_Max,
+};
+Q_DECLARE_METATYPE(WizardButtonType);
+
+#ifdef VBOX_WS_MAC
+/** QWidget-based QFrame analog with one particular purpose to
+  * simulate macOS wizard frame without influencing palette hierarchy. */
+class SHARED_LIBRARY_STUFF UIFrame : public QWidget
+{
+    Q_OBJECT;
+
+public:
+
+    /** Constructs UIFrame passing @a pParent to the base-class. */
+    UIFrame(QWidget *pParent);
+
+protected:
+
+    /** Handles paint @a pEvent. */
+    virtual void paintEvent(QPaintEvent *pEvent) /* final */;
+};
+#endif /* VBOX_WS_MAC */
+
+/** QDialog extension with advanced functionality emulating QWizard behavior. */
+class SHARED_LIBRARY_STUFF UINativeWizard : public QIWithRetranslateUI<QDialog>
+{
+    Q_OBJECT;
+
+public slots:
+
+    /** Executes wizard in window modal mode.
+      * @note You shouldn't have to override it! */
+    virtual int exec() /* final */;
+
+protected:
+
+    /** Constructs wizard passing @a pParent to the base-class.
+      * @param  enmType         Brings the wizard type.
+      * @param  enmMode         Brings the wizard mode.
+      * @param  strHelpHashtag  Brings the wizard help hashtag. */
+    UINativeWizard(QWidget *pParent,
+                   WizardType enmType,
+                   WizardMode enmMode = WizardMode_Auto,
+                   const QString &strHelpHashtag = QString());
+
+    /** Returns wizard type. */
+    WizardType type() const { return m_enmType; }
+    /** Returns wizard mode. */
+    WizardMode mode() const { return m_enmMode; }
+
+    /** Returns wizard button of specified @a enmType. */
+    QPushButton *wizardButton(const WizardButtonType &enmType) const;
+    /** Defines @a strName for wizard button of specified @a enmType. */
+    void setWizardButtonName(const WizardButtonType &enmType, const QString &strName);
+
+    /** Defines pixmap @a strName. */
+    void setPixmapName(const QString &strName);
+
+    /** Appends wizard @a pPage.
+      * @returns assigned page index. */
+    int addPage(UINativeWizardPage *pPage);
+    /** Populates pages.
+      * @note In your subclasses you should add
+      *       pages via addPage declared above. */
+    virtual void populatePages() = 0;
+
+    /** Handles translation event. */
+    virtual void retranslateUi() /* override */;
+
+private slots:
+
+    /** Handles current-page change to page with @a iIndex. */
+    void sltCurrentIndexChanged(int iIndex = -1);
+    /** Handles page validity changes. */
+    void sltCompleteChanged();
+
+    /** Toggles between basic and expert modes. */
+    void sltExpert();
+    /** Switches to previous page. */
+    void sltPrevious();
+    /** Switches to next page. */
+    void sltNext();
+
+private:
+
+    /** Prepares all. */
+    void prepare();
+    /** Cleanups all. */
+    void cleanup();
+    /** Inits all. */
+    void init();
+
+    /** Performs pages translation. */
+    void retranslatePages();
+
+    /** Resizes wizard to golden ratio. */
+    void resizeToGoldenRatio();
+#ifdef VBOX_WS_MAC
+    /** Assigns wizard background. */
+    void assignBackground();
+#else
+    /** Assigns wizard watermark. */
+    void assignWatermark();
+#endif
+
+    /** Holds the wizard type. */
+    WizardType  m_enmType;
+    /** Holds the wizard mode. */
+    WizardMode  m_enmMode;
+    /** Holds the wizard help hashtag. */
+    QString     m_strHelpHashtag;
+    /** Holds the pixmap name. */
+    QString     m_strPixmapName;
+
+    /** Holds the pixmap label instance. */
+    QLabel                               *m_pLabelPixmap;
+#ifdef VBOX_WS_MAC
+    QLabel                               *m_pLabelPageTitle;
+#endif
+    /** Holds the widget-stack instance. */
+    QStackedWidget                       *m_pWidgetStack;
+    /** Holds button instance map. */
+    QMap<WizardButtonType, QPushButton*>  m_buttons;
+};
+
+/** Native wizard interface pointer. */
+typedef QPointer<UINativeWizard> UINativeWizardPointer;
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_UINativeWizard_h */
Index: trunk/src/VBox/Frontends/VirtualBox/src/wizards/UINativeWizardPage.cpp
===================================================================
--- trunk/src/VBox/Frontends/VirtualBox/src/wizards/UINativeWizardPage.cpp	(revision 89960)
+++ trunk/src/VBox/Frontends/VirtualBox/src/wizards/UINativeWizardPage.cpp	(revision 89960)
@@ -0,0 +1,42 @@
+/* $Id$ */
+/** @file
+ * VBox Qt GUI - UINativeWizardPage class implementation.
+ */
+
+/*
+ * Copyright (C) 2009-2021 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/* GUI includes: */
+#include "UINativeWizard.h"
+#include "UINativeWizardPage.h"
+
+
+UINativeWizardPage::UINativeWizardPage()
+{
+}
+
+void UINativeWizardPage::setTitle(const QString &strTitle)
+{
+    m_strTitle = strTitle;
+}
+
+QString UINativeWizardPage::title() const
+{
+    return m_strTitle;
+}
+
+UINativeWizard *UINativeWizardPage::wizard() const
+{
+    return   parentWidget() && parentWidget()->window()
+           ? qobject_cast<UINativeWizard*>(parentWidget()->window())
+           : 0;
+}
Index: trunk/src/VBox/Frontends/VirtualBox/src/wizards/UINativeWizardPage.h
===================================================================
--- trunk/src/VBox/Frontends/VirtualBox/src/wizards/UINativeWizardPage.h	(revision 89960)
+++ trunk/src/VBox/Frontends/VirtualBox/src/wizards/UINativeWizardPage.h	(revision 89960)
@@ -0,0 +1,73 @@
+/* $Id$ */
+/** @file
+ * VBox Qt GUI - UINativeWizardPage class declaration.
+ */
+
+/*
+ * Copyright (C) 2009-2021 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#ifndef FEQT_INCLUDED_SRC_wizards_UINativeWizardPage_h
+#define FEQT_INCLUDED_SRC_wizards_UINativeWizardPage_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* GUI includes: */
+#include "QIWithRetranslateUI.h"
+#include "UILibraryDefs.h"
+
+/* Forward declarations: */
+class UINativeWizard;
+
+/** QWidget extension with advanced functionality emulating QWizardPage behavior. */
+class SHARED_LIBRARY_STUFF UINativeWizardPage : public QIWithRetranslateUI<QWidget>
+{
+    Q_OBJECT;
+
+signals:
+
+    /** Notifies about page validity changes. */
+    void completeChanged();
+
+public:
+
+    /** Constructs wizard page. */
+    UINativeWizardPage();
+
+    /** Redirects the translation call to actual handler. */
+    void retranslate() { retranslateUi(); }
+
+    /** Defines page @a strTitle. */
+    void setTitle(const QString &strTitle);
+    /** Returns page title. */
+    QString title() const;
+
+    /** Handles the page initialization. */
+    virtual void initializePage() {}
+    /** Tests the page for completeness, enables the Next button if Ok.
+      * @returns whether all conditions to go to next page are satisfied. */
+    virtual bool isComplete() const { return true; }
+    /** Tests the page for validity, tranfers to the Next page is Ok.
+      * @returns whether page state to go to next page is bearable. */
+    virtual bool validatePage() { return true; }
+
+protected:
+
+    /** Returns wizard this page belongs to. */
+    UINativeWizard *wizard() const;
+
+    /** Holds the page title. */
+    QString  m_strTitle;
+};
+
+
+#endif /* !FEQT_INCLUDED_SRC_wizards_UINativeWizardPage_h */
