From d33d01fb82129eeee26f137d6f242cb13fa4d61e Mon Sep 17 00:00:00 2001
From: Adam Leung <adamjleung123@gmail.com>
Date: Tue, 3 Aug 2021 18:25:52 +1000
Subject: [PATCH] qt: Complete firstrun wizard

---
 modules/gui/qt/Makefile.am                    |   5 +
 .../qt/dialogs/firstrun/firstrunwizard.cpp    | 360 +++++++++++
 .../qt/dialogs/firstrun/firstrunwizard.hpp    |  68 +++
 .../gui/qt/dialogs/firstrun/firstrunwizard.ui | 560 ++++++++++++++++++
 po/POTFILES.in                                |   2 +
 5 files changed, 995 insertions(+)
 create mode 100644 modules/gui/qt/dialogs/firstrun/firstrunwizard.cpp
 create mode 100644 modules/gui/qt/dialogs/firstrun/firstrunwizard.hpp
 create mode 100644 modules/gui/qt/dialogs/firstrun/firstrunwizard.ui

diff --git a/modules/gui/qt/Makefile.am b/modules/gui/qt/Makefile.am
index 288dac74600b..f6631fa578dc 100644
--- a/modules/gui/qt/Makefile.am
+++ b/modules/gui/qt/Makefile.am
@@ -17,6 +17,7 @@ libqt_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) \
 	-I$(srcdir)/gui/qt -I$(builddir)/gui/qt/ \
 	-I$(builddir)/gui/qt/dialogs/extended \
 	-I$(builddir)/gui/qt/dialogs/fingerprint \
+	-I$(builddir)/gui/qt/dialogs/firstrun \
 	-I$(builddir)/gui/qt/dialogs/help \
 	-I$(builddir)/gui/qt/dialogs/messages \
 	-I$(builddir)/gui/qt/dialogs/open \
@@ -82,6 +83,7 @@ libqt_plugin_la_SOURCES = \
 	gui/qt/dialogs/fingerprint/fingerprintdialog.cpp \
 	gui/qt/dialogs/fingerprint/fingerprintdialog.hpp \
 	gui/qt/dialogs/firstrun/firstrun.cpp gui/qt/dialogs/firstrun/firstrun.hpp \
+	gui/qt/dialogs/firstrun/firstrunwizard.cpp gui/qt/dialogs/firstrun/firstrunwizard.hpp \
 	gui/qt/dialogs/gototime/gototime.cpp gui/qt/dialogs/gototime/gototime.hpp \
 	gui/qt/dialogs/help/aboutmodel.cpp \
 	gui/qt/dialogs/help/aboutmodel.hpp \
@@ -313,6 +315,7 @@ nodist_libqt_plugin_la_SOURCES = \
 	gui/qt/dialogs/fingerprint/chromaprint.moc.cpp \
 	gui/qt/dialogs/fingerprint/fingerprintdialog.moc.cpp \
 	gui/qt/dialogs/firstrun/firstrun.moc.cpp \
+	gui/qt/dialogs/firstrun/firstrunwizard.moc.cpp \
 	gui/qt/dialogs/gototime/gototime.moc.cpp \
 	gui/qt/dialogs/help/aboutmodel.moc.cpp \
 	gui/qt/dialogs/help/help.moc.cpp \
@@ -422,6 +425,7 @@ nodist_libqt_plugin_la_SOURCES += \
 	gui/qt/dialogs/extended/ui_equalizer.h \
 	gui/qt/dialogs/extended/ui_video_effects.h \
 	gui/qt/dialogs/fingerprint/ui_fingerprintdialog.h \
+	gui/qt/dialogs/firstrun/ui_firstrunwizard.h \
 	gui/qt/dialogs/help/ui_about.h \
 	gui/qt/dialogs/help/ui_update.h \
 	gui/qt/dialogs/messages/ui_messages_panel.h \
@@ -462,6 +466,7 @@ libqt_plugin_la_UI = \
 	gui/qt/dialogs/extended/equalizer.ui \
 	gui/qt/dialogs/extended/video_effects.ui \
 	gui/qt/dialogs/fingerprint/fingerprintdialog.ui \
+	gui/qt/dialogs/firstrun/firstrunwizard.ui \
 	gui/qt/dialogs/help/about.ui \
 	gui/qt/dialogs/help/update.ui \
 	gui/qt/dialogs/messages/messages_panel.ui \
diff --git a/modules/gui/qt/dialogs/firstrun/firstrunwizard.cpp b/modules/gui/qt/dialogs/firstrun/firstrunwizard.cpp
new file mode 100644
index 000000000000..c80f7529a94e
--- /dev/null
+++ b/modules/gui/qt/dialogs/firstrun/firstrunwizard.cpp
@@ -0,0 +1,360 @@
+/*****************************************************************************
+ * Copyright (C) 2021 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * ( at your option ) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#include "firstrunwizard.hpp"
+#include "util/color_scheme_model.hpp"
+#include "maininterface/main_interface.hpp"
+#include "dialogs/toolbar/controlbar_profile_model.hpp"
+
+#include <QPushButton>
+#include <QButtonGroup>
+#include <QFileDialog>
+#include <QtCore>
+
+#include <vlc_common.h>
+#include <vlc_input_item.h>
+#include <vlc_url.h>
+
+FirstRunWizard::FirstRunWizard( qt_intf_t *_p_intf, QWidget *parent)
+               : QWizard( parent )
+{
+    p_intf = _p_intf;
+
+    /* Set windown properties */
+    setWindowTitle( qtr( "Welcome" ) );
+    setWindowModality( Qt::WindowModal );
+    setAttribute( Qt::WA_DeleteOnClose );
+
+    /* Build the Ui */
+    ui.setupUi( this );
+
+    /* Set the privacy and network policy */
+    ui.policy->setHtml( qtr( "<p>In order to protect your privacy, <i>VLC media player</i> "
+        "does <b>not</b> collect personal data or transmit them, "
+        "not even in anonymized form, to anyone."
+        "</p>\n"
+        "<p>Nevertheless, <i>VLC</i> is able to automatically retrieve "
+        "information about the media in your playlist from third party "
+        "Internet-based services. This includes cover art, track names, "
+        "artist names and other meta-data."
+        "</p>\n"
+        "<p>Consequently, this may entail identifying some of your media files to third party "
+        "entities. Therefore the <i>VLC</i> developers require your express "
+        "consent for the media player to access the Internet automatically."
+        "</p>\n" ) );
+    ui.policy->setReadOnly( true );
+
+    /* Set up the button group for colour schemes. */
+    /* Creating in Qt Designer has unstable ordering when calling buttons() */
+    colorSchemeGroup = new QButtonGroup( this );
+    colorSchemeGroup->addButton( ui.systemButton );
+    colorSchemeGroup->addButton( ui.lightButton );
+    colorSchemeGroup->addButton( ui.darkButton );
+    colorSchemeGroup->setExclusive( true );
+
+    colorSchemeImages = new QButtonGroup( this );
+    colorSchemeImages->addButton( ui.daynightImage );
+    colorSchemeImages->addButton( ui.lightImage );
+    colorSchemeImages->addButton( ui.darkImage );
+
+    /* Setup the layout page */
+    ui.layoutGroup->setId( ui.modernButton, 0 );
+    ui.layoutGroup->setId( ui.classicButton, 1 );
+
+    layoutImages = new QButtonGroup( this );
+    layoutImages->addButton( ui.modernImage );
+    layoutImages->addButton( ui.classicImage );
+    layoutImages->setId( ui.modernImage, MODERN );
+    layoutImages->setId( ui.classicImage, CLASSIC );
+
+    /* Set the tooltips for each of the layout choices */
+    ui.classicButton->setToolTip( qtr("<ul>"
+                                      "<li>No client-side decoration</li>"
+                                      "<li>Toolbar menu</li>"
+                                      "<li>Pinned video controls</li>"
+                                      "</ul>") );
+
+    ui.modernButton->setToolTip( qtr("<ul>"
+                                     "<li>Client-side decoration</li>"
+                                     "<li>No toolbar menu</li>"
+                                     "<li>Video controls unpinned</li>"
+                                     "</ul>") );
+
+    /* Remove the cancel button */
+    setOption( QWizard::NoCancelButton );
+
+    /* Create new instance of MLFoldersModel */
+    if ( vlc_ml_instance_get( p_intf ) )
+    {
+        const auto foldersModel = new MLFoldersModel( this );
+        foldersModel->setMl( vlc_ml_instance_get( p_intf ) );
+        ui.entryPoints->setMLFoldersModel( foldersModel );
+        mlFoldersEditor = ui.entryPoints;
+        mlFoldersModel = foldersModel;
+    }
+    else
+    {
+        ui.enableMl->setChecked( false );
+    }
+
+    /* Disable the ML button as toggling doesn't actually do anything */
+    ui.enableMl->setEnabled(false);
+
+    /* Slots and Signals */
+    connect( ui.addButton, &QPushButton::clicked, this, &FirstRunWizard::MLaddNewFolder );
+    connect( colorSchemeGroup, qOverload<QAbstractButton*>( &QButtonGroup::buttonClicked ), this, &FirstRunWizard::updateColorLabel );
+    connect( colorSchemeImages, qOverload<QAbstractButton*>( &QButtonGroup::buttonClicked ), this, &FirstRunWizard::imageColorSchemeClick );
+    connect( ui.layoutGroup, qOverload<QAbstractButton*>( &QButtonGroup::buttonClicked ), this, &FirstRunWizard::updateLayoutLabel );
+    connect( layoutImages, qOverload<QAbstractButton*>( &QButtonGroup::buttonClicked ), this, &FirstRunWizard::imageLayoutClick );
+    connect( this->button(QWizard::FinishButton), &QPushButton::clicked, this, &FirstRunWizard::finish );
+}
+
+/**
+ * Function called when the finish button is pressed.
+ * Processes all inputted settings according to chosen options
+ */
+void FirstRunWizard::finish()
+{
+    /* Welcome Page settings  */
+    config_PutInt( "metadata-network-access", ui.privacyCheckbox->isChecked() );
+    config_PutInt( "qt-privacy-ask", 0 );
+
+    /* Colour Page settings */
+    p_intf->p_mi->getColorScheme()->setCurrentIndex( colorSchemeGroup->checkedId() );
+
+    /* Layout Page settings */
+    config_PutInt( "qt-menubar", ui.layoutGroup->checkedId() );
+#if QT_CLIENT_SIDE_DECORATION_AVAILABLE
+    config_PutInt( "qt-titlebar", ui.layoutGroup->checkedId() );
+#endif
+    p_intf->p_mi->setPinVideoControls( ui.layoutGroup->checkedId() );
+
+    ControlbarProfileModel* controlbarModel = p_intf->p_mi->controlbarProfileModel();
+    assert(controlbarModel);
+    if( ui.layoutGroup->checkedId() )
+        controlbarModel->setSelectedProfileFromId(ControlbarProfileModel::CLASSIC_STYLE);
+    else
+        controlbarModel->setSelectedProfileFromId(ControlbarProfileModel::DEFAULT_STYLE);
+
+    /* Commit changes to the scanned folders for the Media Library */
+    if( vlc_ml_instance_get( p_intf ) && mlFoldersEditor )
+        mlFoldersEditor->commit();
+
+    /* Reload the indexing service for the media library */
+    if( p_intf->p_mi->getMediaLibrary() )
+        p_intf->p_mi->getMediaLibrary()->reload();
+
+    p_intf->p_mi->reloadPrefs();
+    p_intf->p_mi->controlbarProfileModel()->save();
+    config_SaveConfigFile( p_intf );
+}
+
+/**
+ * Opens a QFileDialog for the user to select a folder to add to the Media Library
+ * If a folder is selected, add it to the list of folders under MLFoldersEditor
+ */
+void FirstRunWizard::MLaddNewFolder()
+{
+    QUrl newEntryPoint = QFileDialog::getExistingDirectoryUrl( this, qtr("Choose a folder to add to the Media Library"),
+                                                               QUrl( QDir::homePath() ));
+
+    if( !newEntryPoint.isEmpty() )
+        mlFoldersEditor->add( newEntryPoint );
+}
+
+/**
+ * Automatically updates the label on the color scheme page depending
+ * on the currently selected radio button choice
+ * @param id The id of the button we are updating the label of
+ */
+void FirstRunWizard::updateColorLabel( QAbstractButton* btn )
+{
+    switch ( colorSchemeGroup->id(btn) )
+    {
+        case ColorSchemeModel::System:
+            ui.explainerLabel->setText( qtr( "<i>VLC will automatically switch to dark mode accordingly with system settings</i>" ) );
+            break;
+        case ColorSchemeModel::Day:
+            ui.explainerLabel->setText( qtr( "<i>VLC will automatically use light mode</i>" ) );
+            break;
+        case ColorSchemeModel::Night:
+            ui.explainerLabel->setText( qtr( "<i>VLC will automatically use dark mode</i>" ) );
+            break;
+        case ColorSchemeModel::Auto:
+            ui.explainerLabel->setText( qtr( "<i>VLC will automatically switch to dark mode accordingly with system settings</i>") );
+            break;
+    }
+}
+
+/**
+ * Automatically updates the label depending on the currently selected layout
+ * @param id The id of the button we are updating the label of
+ */
+void FirstRunWizard::updateLayoutLabel( QAbstractButton* btn )
+{
+    switch ( ui.layoutGroup->id( btn ) )
+    {
+        case MODERN:
+            ui.layoutExplainer->setText( qtr( "<i>VLC will use a modern layout with no menubar or pinned controls but with client-side decoration</i>" ) );
+            break;
+        case CLASSIC:
+            ui.layoutExplainer->setText( qtr( "<i>VLC will use a classic layout with a menubar and pinned controls but with no client-side decoration</i>" ) );
+            break;
+    }
+}
+
+/**
+ * Checks the correct button when the corresponding image is clicked
+ * for the color scheme page.
+ * @param id The id of the image that was clicked
+ */
+void FirstRunWizard::imageColorSchemeClick( QAbstractButton* btn )
+{
+    QAbstractButton* groupBtn = colorSchemeGroup->buttons().at( colorSchemeImages->id( btn ) );
+    assert( groupBtn );
+    groupBtn->setChecked( true );
+    updateColorLabel( groupBtn );
+}
+
+/**
+ * Checks the correct button when the corresponding image is clicked
+ * for the layouts page.
+ * @param id The id of the image that was clicked
+ */
+void FirstRunWizard::imageLayoutClick( QAbstractButton* btn )
+{
+    QAbstractButton* layoutBtn = ui.layoutGroup->buttons().at( layoutImages->id( btn ) );
+    assert( layoutBtn );
+    layoutBtn->setChecked( true );
+    updateLayoutLabel( layoutBtn );
+}
+
+/**
+ * Defines the navigation of pages for the wizard
+ * @return int - the page id to go to or -1 if we are done
+ */
+int FirstRunWizard::nextId() const
+{
+    switch ( currentId() )
+    {
+        case WELCOME_PAGE:
+            if( ui.enableMl->isChecked() )
+                return FOLDER_PAGE;
+            else
+                return COLOR_SCHEME_PAGE;
+        case FOLDER_PAGE:
+            return COLOR_SCHEME_PAGE;
+        case COLOR_SCHEME_PAGE:
+            return LAYOUT_PAGE;
+        default:
+            return -1;
+    }
+}
+
+/**
+ * Sets up the buttons and options for the color scheme page
+ * Needs to be set up later or else the main interface hasn't been created yet
+ * Color schemes are always in the order system/auto, day then night
+ */
+void FirstRunWizard::initializePage( int id )
+{
+    if(id == COLOR_SCHEME_PAGE)
+    {
+        QVector<ColorSchemeModel::Item> schemes = p_intf->p_mi->getColorScheme()->getSchemes();
+        auto schemeButtons = colorSchemeGroup->buttons();
+        auto schemeImages = colorSchemeImages->buttons();
+
+        /* Number of buttons should be equal to the schemes we can choose from */
+        assert( schemes.size() == schemeButtons.size() );
+        assert( schemes.size() == schemeImages.size() );
+
+        for( int i = 0; i < schemes.size(); i++ )
+        {
+            colorSchemeGroup->setId( schemeButtons.at(i), schemes.at(i).scheme );
+            colorSchemeImages->setId( schemeImages.at(i), schemes.at(i).scheme );
+            schemeButtons.at(i)->setText( schemes.at(i).text );
+            if( !i ) updateColorLabel( schemeButtons.at(i) );
+        }
+    }
+    else if ( id == FOLDER_PAGE )
+        addDefaults();
+}
+
+/**
+ * Processes the default options on rejection of the FirstRun Wizard.
+ * The default options are:
+ * - Yes to metadata
+ * - Default folders in the Media Library
+ * - System/Auto colour scheme
+ * - Modern VLC layout
+ */
+void FirstRunWizard::reject()
+{
+    assert(p_intf->p_mi);
+    /* Welcome Page settings  */
+    config_PutInt( "metadata-network-access", 1 );
+    config_PutInt( "qt-privacy-ask", 0 );
+
+    /* Colour Page settings */
+    p_intf->p_mi->getColorScheme()->setCurrentIndex( p_intf->p_mi->getColorScheme()->getSchemes().at(0).scheme );
+
+    /* Layout Page settings */
+    config_PutInt( "qt-menubar", 0 );
+#if QT_CLIENT_SIDE_DECORATION_AVAILABLE
+    config_PutInt( "qt-titlebar", 0 );
+#endif
+    p_intf->p_mi->setPinVideoControls( 0 );
+    p_intf->p_mi->controlbarProfileModel()->setSelectedProfileFromId(ControlbarProfileModel::DEFAULT_STYLE);
+
+    /* Folders Page settings */
+    if ( mlFoldersEditor )
+    {
+        addDefaults();
+        mlFoldersEditor->commit();
+    }
+
+    if( p_intf->p_mi->getMediaLibrary() )
+        p_intf->p_mi->getMediaLibrary()->reload();
+
+    config_SaveConfigFile( p_intf );
+    p_intf->p_mi->reloadPrefs();
+    p_intf->p_mi->controlbarProfileModel()->save();
+
+    done( QDialog::Rejected );
+}
+
+/**
+ * Adds the default folders to the media library
+ */
+void FirstRunWizard::addDefaults()
+{
+    // Return if we already set the defaults or something is null
+    if( mlDefaults || !mlFoldersEditor || mlFoldersEditor->rowCount() )
+        return;
+
+    for( auto&& target : { VLC_VIDEOS_DIR, VLC_MUSIC_DIR } )
+    {
+        auto folder = vlc::wrap_cptr( config_GetUserDir( target ) );
+        if( folder == nullptr )
+            continue;
+        auto folderMrl = vlc::wrap_cptr( vlc_path2uri( folder.get(), nullptr ) );
+        mlFoldersEditor->add( QUrl( folderMrl.get() ) );
+    }
+
+    mlDefaults = true;
+}
diff --git a/modules/gui/qt/dialogs/firstrun/firstrunwizard.hpp b/modules/gui/qt/dialogs/firstrun/firstrunwizard.hpp
new file mode 100644
index 000000000000..9098890568e2
--- /dev/null
+++ b/modules/gui/qt/dialogs/firstrun/firstrunwizard.hpp
@@ -0,0 +1,68 @@
+/*****************************************************************************
+ * Copyright (C) 2021 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * ( at your option ) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifndef VLC_FIRSTRUNWIZARD_H
+#define VLC_FIRSTRUNWIZARD_H
+
+#include "ui_firstrunwizard.h"
+
+#include <QObject>
+#include <QWizard>
+
+class MLFoldersEditor;
+class MLFoldersModel;
+
+class FirstRunWizard : public QWizard
+{
+    Q_OBJECT
+public:
+    FirstRunWizard ( qt_intf_t*, QWidget* parent = nullptr );
+
+private:
+    enum { WELCOME_PAGE, FOLDER_PAGE, COLOR_SCHEME_PAGE, LAYOUT_PAGE };
+    enum { MODERN, CLASSIC };
+
+    void addDefaults();
+
+    int nextId() const;
+    void initializePage( int id );
+    void reject();
+
+private:
+    Ui::firstrun ui;
+
+    MLFoldersEditor *mlFoldersEditor = nullptr;
+    MLFoldersModel *mlFoldersModel = nullptr;
+
+    qt_intf_t* p_intf;
+    bool mlDefaults = false;
+
+    QButtonGroup* colorSchemeGroup = nullptr;
+    QButtonGroup* colorSchemeImages = nullptr;
+    QButtonGroup* layoutImages = nullptr;
+
+private slots:
+    void finish();
+    void MLaddNewFolder();
+    void updateColorLabel( QAbstractButton* );
+    void updateLayoutLabel (QAbstractButton* );
+    void imageColorSchemeClick ( QAbstractButton* );
+    void imageLayoutClick( QAbstractButton* );
+};
+
+#endif // VLC_FIRSTRUNWIZARD_H
diff --git a/modules/gui/qt/dialogs/firstrun/firstrunwizard.ui b/modules/gui/qt/dialogs/firstrun/firstrunwizard.ui
new file mode 100644
index 000000000000..c70c05a2d38d
--- /dev/null
+++ b/modules/gui/qt/dialogs/firstrun/firstrunwizard.ui
@@ -0,0 +1,560 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>firstrun</class>
+ <widget class="QWizard" name="firstrun">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>800</width>
+    <height>500</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>650</width>
+    <height>400</height>
+   </size>
+  </property>
+  <property name="windowTitle">
+   <string>Welcome</string>
+  </property>
+  <widget class="QWizardPage" name="welcomePage">
+   <property name="minimumSize">
+    <size>
+     <width>320</width>
+     <height>0</height>
+    </size>
+   </property>
+   <layout class="QGridLayout" name="gridLayout">
+    <item row="0" column="0">
+     <widget class="QFrame" name="horizontalFrame_policy">
+      <property name="sizePolicy">
+       <sizepolicy hsizetype="Minimum" vsizetype="MinimumExpanding">
+        <horstretch>0</horstretch>
+        <verstretch>0</verstretch>
+       </sizepolicy>
+      </property>
+      <layout class="QVBoxLayout" name="verticalLayout">
+       <item>
+        <widget class="QLabel" name="privacy">
+         <property name="text">
+          <string>Privacy and Network Access Policy</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QTextEdit" name="policy">
+         <property name="enabled">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QCheckBox" name="privacyCheckbox">
+         <property name="text">
+          <string>Allow metadata network access</string>
+         </property>
+         <property name="checked">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </widget>
+    </item>
+    <item row="1" column="0">
+     <widget class="QFrame" name="horizontalFrame_prefs">
+      <property name="sizePolicy">
+       <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+        <horstretch>0</horstretch>
+        <verstretch>0</verstretch>
+       </sizepolicy>
+      </property>
+      <layout class="QVBoxLayout" name="verticalLayout_3">
+       <item>
+        <widget class="QLabel" name="pref">
+         <property name="text">
+          <string>Preferences</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QLabel" name="label">
+         <property name="text">
+          <string>&lt;small&gt;For a full list of settings, see the &lt;b&gt;preferences&lt;/b&gt; tab&lt;/small&gt;</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QCheckBox" name="enableMl">
+         <property name="text">
+          <string>Enable the Media Library</string>
+         </property>
+         <property name="checked">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QWizardPage" name="folderPage">
+   <layout class="QGridLayout" name="gridLayout_2">
+    <item row="0" column="0">
+     <widget class="QLabel" name="folderLabel">
+      <property name="text">
+       <string>Folders scanned by the Media Library</string>
+      </property>
+     </widget>
+    </item>
+    <item row="0" column="1">
+     <widget class="QPushButton" name="addButton">
+      <property name="maximumSize">
+       <size>
+        <width>16</width>
+        <height>16</height>
+       </size>
+      </property>
+      <property name="text">
+       <string>+</string>
+      </property>
+     </widget>
+    </item>
+    <item row="1" column="0" colspan="2">
+     <widget class="MLFoldersEditor" name="entryPoints">
+      <property name="editTriggers">
+       <set>QAbstractItemView::AllEditTriggers</set>
+      </property>
+      <property name="alternatingRowColors">
+       <bool>true</bool>
+      </property>
+      <property name="showGrid">
+       <bool>false</bool>
+      </property>
+      <property name="rowCount">
+       <number>1</number>
+      </property>
+      <attribute name="horizontalHeaderShowSortIndicator" stdset="0">
+       <bool>false</bool>
+      </attribute>
+      <row/>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QWizardPage" name="colorSchemePage">
+   <layout class="QGridLayout" name="gridLayout_3">
+    <item row="1" column="0">
+     <spacer name="verticalSpacer">
+      <property name="orientation">
+       <enum>Qt::Vertical</enum>
+      </property>
+      <property name="sizeType">
+       <enum>QSizePolicy::MinimumExpanding</enum>
+      </property>
+      <property name="sizeHint" stdset="0">
+       <size>
+        <width>20</width>
+        <height>20</height>
+       </size>
+      </property>
+     </spacer>
+    </item>
+    <item row="2" column="1">
+     <layout class="QVBoxLayout" name="verticalLayout_4">
+      <item alignment="Qt::AlignHCenter">
+       <widget class="QRadioButton" name="lightButton">
+        <property name="text">
+         <string>Light</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <spacer name="verticalSpacer_3">
+        <property name="orientation">
+         <enum>Qt::Vertical</enum>
+        </property>
+        <property name="sizeType">
+         <enum>QSizePolicy::Maximum</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>20</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+      <item>
+       <widget class="QPushButton" name="lightImage">
+        <property name="text">
+         <string/>
+        </property>
+        <property name="icon">
+         <iconset resource="../../vlc.qrc">
+          <normaloff>:/theme_light.svg</normaloff>:/theme_light.svg</iconset>
+        </property>
+        <property name="iconSize">
+         <size>
+          <width>87</width>
+          <height>154</height>
+         </size>
+        </property>
+        <property name="flat">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </item>
+    <item row="2" column="2">
+     <layout class="QVBoxLayout" name="verticalLayout_5">
+      <item alignment="Qt::AlignHCenter">
+       <widget class="QRadioButton" name="darkButton">
+        <property name="text">
+         <string>Dark</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <spacer name="verticalSpacer_4">
+        <property name="orientation">
+         <enum>Qt::Vertical</enum>
+        </property>
+        <property name="sizeType">
+         <enum>QSizePolicy::Maximum</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>20</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+      <item>
+       <widget class="QPushButton" name="darkImage">
+        <property name="text">
+         <string/>
+        </property>
+        <property name="icon">
+         <iconset resource="../../vlc.qrc">
+          <normaloff>:/theme_dark.svg</normaloff>:/theme_dark.svg</iconset>
+        </property>
+        <property name="iconSize">
+         <size>
+          <width>87</width>
+          <height>154</height>
+         </size>
+        </property>
+        <property name="flat">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </item>
+    <item row="0" column="0">
+     <widget class="QLabel" name="colorLabel">
+      <property name="text">
+       <string>Select a color scheme</string>
+      </property>
+     </widget>
+    </item>
+    <item row="2" column="0">
+     <layout class="QVBoxLayout" name="verticalLayout_2">
+      <item alignment="Qt::AlignHCenter">
+       <widget class="QRadioButton" name="systemButton">
+        <property name="text">
+         <string>System </string>
+        </property>
+        <property name="checked">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <spacer name="verticalSpacer_2">
+        <property name="orientation">
+         <enum>Qt::Vertical</enum>
+        </property>
+        <property name="sizeType">
+         <enum>QSizePolicy::Maximum</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>20</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+      <item>
+       <widget class="QPushButton" name="daynightImage">
+        <property name="text">
+         <string/>
+        </property>
+        <property name="icon">
+         <iconset resource="../../vlc.qrc">
+          <normaloff>:/theme_daynight.svg</normaloff>:/theme_daynight.svg</iconset>
+        </property>
+        <property name="iconSize">
+         <size>
+          <width>87</width>
+          <height>154</height>
+         </size>
+        </property>
+        <property name="flat">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </item>
+    <item row="3" column="1">
+     <spacer name="verticalSpacer_5">
+      <property name="orientation">
+       <enum>Qt::Vertical</enum>
+      </property>
+      <property name="sizeType">
+       <enum>QSizePolicy::Maximum</enum>
+      </property>
+      <property name="sizeHint" stdset="0">
+       <size>
+        <width>20</width>
+        <height>10</height>
+       </size>
+      </property>
+     </spacer>
+    </item>
+    <item row="4" column="0" colspan="3">
+     <widget class="QLabel" name="explainerLabel">
+      <property name="text">
+       <string>&lt;i&gt;VLC will automatically switch to dark mode accordingly with system settings&lt;/i&gt;</string>
+      </property>
+      <property name="scaledContents">
+       <bool>false</bool>
+      </property>
+      <property name="alignment">
+       <set>Qt::AlignCenter</set>
+      </property>
+      <property name="wordWrap">
+       <bool>true</bool>
+      </property>
+     </widget>
+    </item>
+    <item row="5" column="0">
+     <spacer name="verticalSpacer_10">
+      <property name="orientation">
+       <enum>Qt::Vertical</enum>
+      </property>
+      <property name="sizeType">
+       <enum>QSizePolicy::MinimumExpanding</enum>
+      </property>
+      <property name="sizeHint" stdset="0">
+       <size>
+        <width>20</width>
+        <height>40</height>
+       </size>
+      </property>
+     </spacer>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QWizardPage" name="layoutPage">
+   <layout class="QGridLayout" name="gridLayout_4">
+    <item row="4" column="0" colspan="2">
+     <spacer name="verticalSpacer_7">
+      <property name="orientation">
+       <enum>Qt::Vertical</enum>
+      </property>
+      <property name="sizeType">
+       <enum>QSizePolicy::Minimum</enum>
+      </property>
+      <property name="sizeHint" stdset="0">
+       <size>
+        <width>20</width>
+        <height>20</height>
+       </size>
+      </property>
+     </spacer>
+    </item>
+    <item row="1" column="0" colspan="2">
+     <spacer name="verticalSpacer_6">
+      <property name="orientation">
+       <enum>Qt::Vertical</enum>
+      </property>
+      <property name="sizeType">
+       <enum>QSizePolicy::MinimumExpanding</enum>
+      </property>
+      <property name="sizeHint" stdset="0">
+       <size>
+        <width>20</width>
+        <height>20</height>
+       </size>
+      </property>
+     </spacer>
+    </item>
+    <item row="0" column="0">
+     <widget class="QLabel" name="layoutLabel">
+      <property name="text">
+       <string>Select a layout</string>
+      </property>
+     </widget>
+    </item>
+    <item row="2" column="0">
+     <layout class="QVBoxLayout" name="verticalLayout_6">
+      <item alignment="Qt::AlignHCenter">
+       <widget class="QRadioButton" name="modernButton">
+        <property name="text">
+         <string>Modern</string>
+        </property>
+        <property name="checked">
+         <bool>true</bool>
+        </property>
+        <attribute name="buttonGroup">
+         <string notr="true">layoutGroup</string>
+        </attribute>
+       </widget>
+      </item>
+      <item>
+       <spacer name="verticalSpacer_8">
+        <property name="orientation">
+         <enum>Qt::Vertical</enum>
+        </property>
+        <property name="sizeType">
+         <enum>QSizePolicy::Maximum</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>0</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+      <item>
+       <widget class="QPushButton" name="modernImage">
+        <property name="text">
+         <string/>
+        </property>
+        <property name="icon">
+         <iconset resource="../../vlc.qrc">
+          <normaloff>:/prefsmenu/sample_minimal.png</normaloff>:/prefsmenu/sample_minimal.png</iconset>
+        </property>
+        <property name="iconSize">
+         <size>
+          <width>244</width>
+          <height>145</height>
+         </size>
+        </property>
+        <property name="flat">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </item>
+    <item row="2" column="1">
+     <layout class="QVBoxLayout" name="verticalLayout_7">
+      <item alignment="Qt::AlignHCenter">
+       <widget class="QRadioButton" name="classicButton">
+        <property name="text">
+         <string>Classic</string>
+        </property>
+        <attribute name="buttonGroup">
+         <string notr="true">layoutGroup</string>
+        </attribute>
+       </widget>
+      </item>
+      <item>
+       <spacer name="verticalSpacer_9">
+        <property name="orientation">
+         <enum>Qt::Vertical</enum>
+        </property>
+        <property name="sizeType">
+         <enum>QSizePolicy::Maximum</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>0</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+      <item>
+       <widget class="QPushButton" name="classicImage">
+        <property name="text">
+         <string/>
+        </property>
+        <property name="icon">
+         <iconset resource="../../vlc.qrc">
+          <normaloff>:/prefsmenu/sample_complete.png</normaloff>:/prefsmenu/sample_complete.png</iconset>
+        </property>
+        <property name="iconSize">
+         <size>
+          <width>244</width>
+          <height>145</height>
+         </size>
+        </property>
+        <property name="flat">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </item>
+    <item row="5" column="0" colspan="2">
+     <widget class="QLabel" name="layoutExplainer">
+      <property name="text">
+       <string>&lt;i&gt;VLC will use a modern layout with no menubar or pinned controls but with client-side decoration&lt;/i&gt;</string>
+      </property>
+      <property name="alignment">
+       <set>Qt::AlignCenter</set>
+      </property>
+     </widget>
+    </item>
+    <item row="6" column="0">
+     <spacer name="verticalSpacer_11">
+      <property name="orientation">
+       <enum>Qt::Vertical</enum>
+      </property>
+      <property name="sizeType">
+       <enum>QSizePolicy::MinimumExpanding</enum>
+      </property>
+      <property name="sizeHint" stdset="0">
+       <size>
+        <width>20</width>
+        <height>40</height>
+       </size>
+      </property>
+     </spacer>
+    </item>
+   </layout>
+  </widget>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>MLFoldersEditor</class>
+   <extends>QTableWidget</extends>
+   <header>widgets/native/mlfolderseditor.hpp</header>
+  </customwidget>
+ </customwidgets>
+ <resources>
+  <include location="../../vlc.qrc"/>
+ </resources>
+ <connections/>
+ <buttongroups>
+  <buttongroup name="layoutGroup"/>
+ </buttongroups>
+</ui>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index e81bfc351429..63015333eb10 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -733,6 +733,8 @@ modules/gui/qt/dialogs/fingerprint/fingerprintdialog.cpp
 modules/gui/qt/dialogs/fingerprint/fingerprintdialog.hpp
 modules/gui/qt/dialogs/firstrun/firstrun.cpp
 modules/gui/qt/dialogs/firstrun/firstrun.hpp
+modules/gui/qt/dialogs/firstrun/firstrunwizard.cpp
+modules/gui/qt/dialogs/firstrun/firstrunwizard.hpp
 modules/gui/qt/dialogs/gototime/gototime.cpp
 modules/gui/qt/dialogs/gototime/gototime.hpp
 modules/gui/qt/dialogs/help/help.cpp
-- 
GitLab