Bläddra i källkod

Split out wizard welcome page

Signed-off-by: Felix Weilbach <felix.weilbach@nextcloud.com>
Felix Weilbach 5 år sedan
förälder
incheckning
9185956069

+ 2 - 1
NEXTCLOUD.cmake

@@ -34,7 +34,8 @@ option( WITH_PROVIDERS "Build with providers list" ON )
 
 
 ## Theming options
-set( APPLICATION_WIZARD_HEADER_BACKGROUND_COLOR "#0082c9" CACHE STRING "Hex color of the wizard header background")
+set(NEXTCLOUD_BACKGROUND_COLOR "#0082c9" CACHE STRING "Default Nextcloud background color")
+set( APPLICATION_WIZARD_HEADER_BACKGROUND_COLOR ${NEXTCLOUD_BACKGROUND_COLOR} CACHE STRING "Hex color of the wizard header background")
 set( APPLICATION_WIZARD_HEADER_TITLE_COLOR "#ffffff" CACHE STRING "Hex color of the text in the wizard header")
 option( APPLICATION_WIZARD_USE_CUSTOM_LOGO "Use the logo from ':/client/theme/colored/wizard_logo.(png|svg)' else the default application icon is used" ON )
 

+ 1 - 0
config.h.in

@@ -25,6 +25,7 @@
 #cmakedefine APPLICATION_SERVER_URL_ENFORCE "@APPLICATION_SERVER_URL_ENFORCE@"
 #cmakedefine LINUX_APPLICATION_ID "@LINUX_APPLICATION_ID@"
 #cmakedefine APPLICATION_WIZARD_HEADER_BACKGROUND_COLOR "@APPLICATION_WIZARD_HEADER_BACKGROUND_COLOR@"
+#cmakedefine NEXTCLOUD_BACKGROUND_COLOR "@NEXTCLOUD_BACKGROUND_COLOR@"
 #cmakedefine APPLICATION_WIZARD_HEADER_TITLE_COLOR "@APPLICATION_WIZARD_HEADER_TITLE_COLOR@"
 #cmakedefine APPLICATION_WIZARD_USE_CUSTOM_LOGO "@APPLICATION_WIZARD_USE_CUSTOM_LOGO@"
 #cmakedefine APPLICATION_VIRTUALFILE_SUFFIX "@APPLICATION_VIRTUALFILE_SUFFIX@"

+ 3 - 0
src/gui/CMakeLists.txt

@@ -51,6 +51,7 @@ set(client_UI_SRCS
     wizard/owncloudsetupnocredspage.ui
     wizard/owncloudwizardresultpage.ui
     wizard/webview.ui
+    wizard/welcomepage.ui
 )
 
 set(client_SRCS
@@ -134,6 +135,8 @@ set(client_SRCS
     wizard/webviewpage.cpp
     wizard/webview.cpp
     wizard/slideshow.cpp
+    wizard/welcomepage.cpp
+    wizard/linklabel.cpp
 )
 
 IF(BUILD_UPDATER)

+ 6 - 1
src/gui/owncloudsetupwizard.cpp

@@ -131,7 +131,12 @@ void OwncloudSetupWizard::startWizard()
 
     _ocWizard->setRemoteFolder(_remoteFolder);
 
-    _ocWizard->setStartId(WizardCommon::Page_ServerSetup);
+#ifdef WITH_PROVIDERS
+    const auto startPage = WizardCommon::Page_Welcome;
+#else // WITH_PROVIDERS
+    const auto startPage = WizardCommon::Page_ServerSetup;
+#endif // WITH_PROVIDERS
+    _ocWizard->setStartId(startPage);
 
     _ocWizard->restart();
 

+ 58 - 0
src/gui/wizard/linklabel.cpp

@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2021 by Felix Weilbach <felix.weilbach@nextcloud.com>
+ *
+ * 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.
+ */
+
+#include "linklabel.h"
+#include "guiutility.h"
+
+namespace OCC {
+
+LinkLabel::LinkLabel(QWidget *parent) : QLabel(parent)
+{
+
+}
+
+void LinkLabel::setUrl(const QUrl &url)
+{
+    this->url = url;
+}
+
+void LinkLabel::enterEvent(QEvent * /*event*/)
+{
+    setFontUnderline(true);
+    setCursor(Qt::PointingHandCursor);
+}
+
+void LinkLabel::leaveEvent(QEvent * /*event*/)
+{
+    setFontUnderline(false);
+    setCursor(Qt::ArrowCursor);
+}
+
+void LinkLabel::mouseReleaseEvent(QMouseEvent * /*event*/)
+{
+    if (url.isValid()) {
+        Utility::openBrowser(url);
+    }
+
+    emit clicked();
+}
+
+void LinkLabel::setFontUnderline(bool value)
+{
+    auto labelFont = font();
+    labelFont.setUnderline(value);
+    setFont(labelFont);
+}
+
+}

+ 46 - 0
src/gui/wizard/linklabel.h

@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 by Felix Weilbach <felix.weilbach@nextcloud.com>
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <QLabel>
+#include <QUrl>
+
+namespace OCC {
+
+class LinkLabel : public QLabel
+{
+    Q_OBJECT
+public:
+    explicit LinkLabel(QWidget *parent = nullptr);
+
+    void setUrl(const QUrl &url);
+
+signals:
+    void clicked();
+
+protected:
+    void enterEvent(QEvent *event) override;
+
+    void leaveEvent(QEvent *event) override;
+
+    void mouseReleaseEvent(QMouseEvent *event) override;
+
+private:
+    void setFontUnderline(bool value);
+
+    QUrl url;
+};
+
+}

+ 36 - 1
src/gui/wizard/owncloudwizard.cpp

@@ -20,6 +20,7 @@
 #include "owncloudgui.h"
 
 #include "wizard/owncloudwizard.h"
+#include "wizard/welcomepage.h"
 #include "wizard/owncloudsetuppage.h"
 #include "wizard/owncloudhttpcredspage.h"
 #include "wizard/owncloudoauthcredspage.h"
@@ -46,6 +47,9 @@ Q_LOGGING_CATEGORY(lcWizard, "nextcloud.gui.wizard", QtInfoMsg)
 OwncloudWizard::OwncloudWizard(QWidget *parent)
     : QWizard(parent)
     , _account(nullptr)
+#ifdef WITH_PROVIDERS
+    , _welcomePage(new WelcomePage(this))
+#endif // WITH_PROVIDERS
     , _setupPage(new OwncloudSetupPage(this))
     , _httpCredsPage(new OwncloudHttpCredsPage(this))
     , _browserCredsPage(new OwncloudOAuthCredsPage)
@@ -57,6 +61,9 @@ OwncloudWizard::OwncloudWizard(QWidget *parent)
     setObjectName("owncloudWizard");
 
     setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
+#ifdef WITH_PROVIDERS
+    setPage(WizardCommon::Page_Welcome, _welcomePage);
+#endif // WITH_PROVIDERS
     setPage(WizardCommon::Page_ServerSetup, _setupPage);
     setPage(WizardCommon::Page_HttpCreds, _httpCredsPage);
     setPage(WizardCommon::Page_OAuthCreds, _browserCredsPage);
@@ -94,6 +101,12 @@ OwncloudWizard::OwncloudWizard(QWidget *parent)
     setSubTitleFormat(Qt::RichText);
     setButtonText(QWizard::CustomButton1, tr("Skip folders configuration"));
 
+    // Change the next buttons size policy since we hide it on the
+    // welcome page but want it to fill it's space that we don't get
+    // flickering when the page changes
+    auto nextButtonSizePolicy = button(QWizard::NextButton)->sizePolicy();
+    nextButtonSizePolicy.setRetainSizeWhenHidden(true);
+    button(QWizard::NextButton)->setSizePolicy(nextButtonSizePolicy);
 
     // Connect styleChanged events to our widgets, so they can adapt (Dark-/Light-Mode switching)
     connect(this, &OwncloudWizard::styleChanged, _setupPage, &OwncloudSetupPage::slotStyleChanged);
@@ -220,6 +233,29 @@ void OwncloudWizard::slotCurrentPageChanged(int id)
 {
     qCDebug(lcWizard) << "Current Wizard page changed to " << id;
 
+    const auto setNextButtonAsDefault = [this]() {
+        auto nextButton = qobject_cast<QPushButton *>(button(QWizard::NextButton));
+        if (nextButton) {
+            nextButton->setDefault(true);
+            nextButton->setFocus();
+        }
+    };
+
+    if (id == WizardCommon::Page_Welcome) {
+        // Set next button to just hidden so it retains it's layout
+        button(QWizard::NextButton)->setHidden(true);
+        // Need to set it from here, otherwise it has no effect
+        _welcomePage->setLoginButtonDefault();
+    } else if (id == WizardCommon::Page_WebView || id == WizardCommon::Page_Flow2AuthCreds) {
+        setButtonLayout({ QWizard::Stretch, QWizard::BackButton });
+    } else if (id == WizardCommon::Page_AdvancedSetup) {
+        setButtonLayout({ QWizard::Stretch, QWizard::CustomButton1, QWizard::BackButton, QWizard::NextButton });
+        setNextButtonAsDefault();
+    } else {
+        setButtonLayout({ QWizard::Stretch, QWizard::BackButton, QWizard::NextButton });
+        setNextButtonAsDefault();
+    }
+
     if (id == WizardCommon::Page_ServerSetup) {
         emit clearPendingRequests();
     }
@@ -232,7 +268,6 @@ void OwncloudWizard::slotCurrentPageChanged(int id)
         done(Accepted);
     }
 
-    setOption(QWizard::HaveCustomButton1, id == WizardCommon::Page_AdvancedSetup);
     if (id == WizardCommon::Page_AdvancedSetup && (_credentialsPage == _browserCredsPage || _credentialsPage == _flow2CredsPage)) {
         // For OAuth, disable the back button in the Page_AdvancedSetup because we don't want
         // to re-open the browser.

+ 2 - 0
src/gui/wizard/owncloudwizard.h

@@ -29,6 +29,7 @@ namespace OCC {
 
 Q_DECLARE_LOGGING_CATEGORY(lcWizard)
 
+class WelcomePage;
 class OwncloudSetupPage;
 class OwncloudHttpCredsPage;
 class OwncloudOAuthCredsPage;
@@ -115,6 +116,7 @@ private:
     void customizeStyle();
 
     AccountPtr _account;
+    WelcomePage *_welcomePage;
     OwncloudSetupPage *_setupPage;
     OwncloudHttpCredsPage *_httpCredsPage;
     OwncloudOAuthCredsPage *_browserCredsPage;

+ 13 - 0
src/gui/wizard/owncloudwizardcommon.cpp

@@ -16,6 +16,10 @@
 #include <QLabel>
 #include <QPixmap>
 #include <QVariant>
+#include <QRadioButton>
+#include <QAbstractButton>
+#include <QCheckBox>
+#include <QSpinBox>
 
 #include "wizard/owncloudwizardcommon.h"
 #include "theme.h"
@@ -68,6 +72,15 @@ namespace WizardCommon {
         errorLabel->setVisible(false);
     }
 
+    void customizeHintLabel(QLabel *label)
+    {
+        auto palette = label->palette();
+        QColor textColor = palette.color(QPalette::Text);
+        textColor.setAlpha(128);
+        palette.setColor(QPalette::Text, textColor);
+        label->setPalette(palette);
+    }
+
 } // ns WizardCommon
 
 } // namespace OCC

+ 6 - 0
src/gui/wizard/owncloudwizardcommon.h

@@ -18,6 +18,10 @@
 
 class QVariant;
 class QLabel;
+class QRadioButton;
+class QSpinBox;
+class QCheckBox;
+class QAbstractButton;
 
 namespace OCC {
 
@@ -27,6 +31,7 @@ namespace WizardCommon {
     QString titleTemplate();
     QString subTitleTemplate();
     void initErrorLabel(QLabel *errorLabel);
+    void customizeHintLabel(QLabel *label);
 
     enum SyncMode {
         SelectiveMode,
@@ -34,6 +39,7 @@ namespace WizardCommon {
     };
 
     enum Pages {
+        Page_Welcome,
         Page_ServerSetup,
         Page_HttpCreds,
         Page_ShibbolethCreds,

+ 120 - 0
src/gui/wizard/welcomepage.cpp

@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2021 by Felix Weilbach <felix.weilbach@nextcloud.com>
+ *
+ * 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.
+ */
+
+#include "welcomepage.h"
+#include "theme.h"
+#include "wizard/owncloudwizard.h"
+#include "wizard/slideshow.h"
+#include "ui_welcomepage.h"
+
+namespace OCC {
+
+WelcomePage::WelcomePage(OwncloudWizard *ocWizard)
+    : QWizardPage()
+    , _ui(new Ui::WelcomePage)
+    , _ocWizard(ocWizard)
+{
+    setupUi();
+}
+
+WelcomePage::~WelcomePage() = default;
+
+void WelcomePage::setupUi()
+{
+    _ui->setupUi(this);
+    setupSlideShow();
+    setupLoginButton();
+    setupCreateAccountButton();
+    setupHostYourOwnServerLabel();
+}
+
+void WelcomePage::initializePage()
+{
+    customizeStyle();
+}
+
+void WelcomePage::setLoginButtonDefault()
+{
+    _ui->loginButton->setDefault(true);
+    _ui->loginButton->setFocus();
+}
+
+void WelcomePage::styleSlideShow()
+{
+    const auto theme = Theme::instance();
+    const auto backgroundColor = palette().window().color();
+
+    const auto wizardNextcloudIconFileName = theme->isBranded() ? Theme::hidpiFileName("wizard-nextcloud.png", backgroundColor)
+                                                                : Theme::hidpiFileName(":/client/theme/colored/wizard-nextcloud.png");
+    const auto wizardFilesIconFileName = theme->isBranded() ? Theme::hidpiFileName("wizard-files.png", backgroundColor)
+                                                            : Theme::hidpiFileName(":/client/theme/colored/wizard-files.png");
+    const auto wizardGroupwareIconFileName = theme->isBranded() ? Theme::hidpiFileName("wizard-groupware.png", backgroundColor)
+                                                                : Theme::hidpiFileName(":/client/theme/colored/wizard-groupware.png");
+    const auto wizardTalkIconFileName = theme->isBranded() ? Theme::hidpiFileName("wizard-talk.png", backgroundColor)
+                                                           : Theme::hidpiFileName(":/client/theme/colored/wizard-talk.png");
+
+    _ui->slideShow->addSlide(wizardNextcloudIconFileName, tr("Keep your data secure and under your control"));
+    _ui->slideShow->addSlide(wizardFilesIconFileName, tr("Secure collaboration & file exchange"));
+    _ui->slideShow->addSlide(wizardGroupwareIconFileName, tr("Easy-to-use web mail, calendaring & contacts"));
+    _ui->slideShow->addSlide(wizardTalkIconFileName, tr("Screensharing, online meetings & web conferences"));
+
+    const auto isDarkBackground = Theme::isDarkColor(backgroundColor);
+    _ui->slideShowNextButton->setIcon(theme->uiThemeIcon(QString("control-next.svg"), isDarkBackground));
+    _ui->slideShowPreviousButton->setIcon(theme->uiThemeIcon(QString("control-prev.svg"), isDarkBackground));
+}
+
+void WelcomePage::setupSlideShow()
+{
+    connect(_ui->slideShow, &SlideShow::clicked, _ui->slideShow, &SlideShow::stopShow);
+    connect(_ui->slideShowNextButton, &QPushButton::clicked, _ui->slideShow, &SlideShow::nextSlide);
+    connect(_ui->slideShowPreviousButton, &QPushButton::clicked, _ui->slideShow, &SlideShow::prevSlide);
+}
+
+void WelcomePage::setupLoginButton()
+{
+    const auto appName = Theme::instance()->appNameGUI();
+
+    _ui->loginButton->setText(tr("Log in to your %1").arg(appName));
+    connect(_ui->loginButton, &QPushButton::clicked, this, [this](bool /*checked*/) {
+        _nextPage = WizardCommon::Page_ServerSetup;
+        _ocWizard->next();
+    });
+}
+
+void WelcomePage::setupCreateAccountButton()
+{
+    connect(_ui->createAccountButton, &QPushButton::clicked, this, [this](bool /*checked*/) {
+        _ocWizard->setRegistration(true);
+        _nextPage = WizardCommon::Page_WebView;
+        _ocWizard->next();
+    });
+}
+
+void WelcomePage::setupHostYourOwnServerLabel()
+{
+    _ui->hostYourOwnServerLabel->setText(tr("Host your own server"));
+    _ui->hostYourOwnServerLabel->setAlignment(Qt::AlignCenter);
+    _ui->hostYourOwnServerLabel->setUrl(QUrl("https://docs.nextcloud.com/server/latest/admin_manual/installation/#installation"));
+}
+
+int WelcomePage::nextId() const
+{
+    return _nextPage;
+}
+
+void WelcomePage::customizeStyle()
+{
+    styleSlideShow();
+}
+}

+ 54 - 0
src/gui/wizard/welcomepage.h

@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2021 by Felix Weilbach <felix.weilbach@nextcloud.com>
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <QWizardPage>
+
+#include "wizard/owncloudwizardcommon.h"
+
+namespace OCC {
+
+class OwncloudWizard;
+
+namespace Ui {
+    class WelcomePage;
+}
+
+class WelcomePage : public QWizardPage
+{
+    Q_OBJECT
+
+public:
+    explicit WelcomePage(OwncloudWizard *ocWizard);
+    ~WelcomePage() override;
+    int nextId() const override;
+    void initializePage() override;
+    void setLoginButtonDefault();
+
+private:
+    void setupUi();
+    void customizeStyle();
+    void styleSlideShow();
+    void setupSlideShow();
+    void setupLoginButton();
+    void setupCreateAccountButton();
+    void setupHostYourOwnServerLabel();
+
+    QScopedPointer<Ui::WelcomePage> _ui;
+
+    OwncloudWizard *_ocWizard;
+    WizardCommon::Pages _nextPage = WizardCommon::Page_ServerSetup;
+};
+}

+ 229 - 0
src/gui/wizard/welcomepage.ui

@@ -0,0 +1,229 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>OCC::WelcomePage</class>
+ <widget class="QWidget" name="OCC::WelcomePage">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>500</width>
+    <height>500</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_2">
+   <item>
+    <spacer name="verticalSpacer_3">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeType">
+      <enum>QSizePolicy::Expanding</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>80</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_2">
+     <property name="topMargin">
+      <number>0</number>
+     </property>
+     <item>
+      <widget class="QPushButton" name="slideShowPreviousButton">
+       <property name="maximumSize">
+        <size>
+         <width>40</width>
+         <height>16777215</height>
+        </size>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+       <property name="autoDefault">
+        <bool>false</bool>
+       </property>
+       <property name="default">
+        <bool>false</bool>
+       </property>
+       <property name="flat">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="OCC::SlideShow" name="slideShow" native="true">
+       <property name="font">
+        <font>
+         <pointsize>12</pointsize>
+         <weight>75</weight>
+         <bold>true</bold>
+        </font>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="slideShowNextButton">
+       <property name="maximumSize">
+        <size>
+         <width>40</width>
+         <height>16777215</height>
+        </size>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+       <property name="autoDefault">
+        <bool>false</bool>
+       </property>
+       <property name="default">
+        <bool>false</bool>
+       </property>
+       <property name="flat">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <property name="leftMargin">
+      <number>0</number>
+     </property>
+     <property name="topMargin">
+      <number>0</number>
+     </property>
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <layout class="QVBoxLayout" name="verticalLayout">
+       <property name="topMargin">
+        <number>0</number>
+       </property>
+       <item>
+        <spacer name="verticalSpacer">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>40</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+       <item>
+        <widget class="QPushButton" name="loginButton">
+         <property name="text">
+          <string>Log in to your %1</string>
+         </property>
+         <property name="autoDefault">
+          <bool>true</bool>
+         </property>
+         <property name="default">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QPushButton" name="createAccountButton">
+         <property name="text">
+          <string>Create account with Provider</string>
+         </property>
+         <property name="autoDefault">
+          <bool>true</bool>
+         </property>
+         <property name="default">
+          <bool>false</bool>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="OCC::LinkLabel" name="hostYourOwnServerLabel" native="true"/>
+       </item>
+       <item>
+        <spacer name="verticalSpacer_5">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>40</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer_2">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <spacer name="verticalSpacer_4">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>40</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>OCC::SlideShow</class>
+   <extends>QWidget</extends>
+   <header>wizard/slideshow.h</header>
+  </customwidget>
+  <customwidget>
+   <class>OCC::LinkLabel</class>
+   <extends>QWidget</extends>
+   <header>wizard/linklabel.h</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>

+ 40 - 2
src/libsync/theme.cpp

@@ -246,6 +246,12 @@ QString Theme::themeImagePath(const QString &name, int size, bool sysTray) const
     }
 }
 
+bool Theme::isHidpi(QPaintDevice *dev)
+{
+    const auto devicePixelRatio = dev ? dev->devicePixelRatio() : qApp->primaryScreen()->devicePixelRatio();
+    return devicePixelRatio > 1;
+}
+
 QIcon Theme::uiThemeIcon(const QString &iconName, bool uiHasDarkBg) const
 {
     QString themeResBasePath = ":/client/theme/";
@@ -256,8 +262,7 @@ QIcon Theme::uiThemeIcon(const QString &iconName, bool uiHasDarkBg) const
 
 QString Theme::hidpiFileName(const QString &fileName, QPaintDevice *dev)
 {
-    qreal devicePixelRatio = dev ? dev->devicePixelRatio() : qApp->primaryScreen()->devicePixelRatio();
-    if (devicePixelRatio <= 1.0) {
+    if (!Theme::isHidpi(dev)) {
         return fileName;
     }
     // try to find a 2x version
@@ -274,6 +279,16 @@ QString Theme::hidpiFileName(const QString &fileName, QPaintDevice *dev)
     return fileName;
 }
 
+QString Theme::hidpiFileName(const QString &iconName, const QColor &backgroundColor, QPaintDevice *dev)
+{
+    const auto isDarkBackground = Theme::isDarkColor(backgroundColor);
+
+    const QString themeResBasePath = ":/client/theme/";
+    const QString iconPath = themeResBasePath + (isDarkBackground ? "white/" : "black/") + iconName;
+
+    return Theme::hidpiFileName(iconPath, dev);
+}
+
 
 #endif
 
@@ -541,6 +556,29 @@ QColor Theme::wizardHeaderBackgroundColor() const
     return {APPLICATION_WIZARD_HEADER_BACKGROUND_COLOR};
 }
 
+QPixmap Theme::wizardApplicationLogo() const
+{
+    if (!Theme::isBranded()) {
+        return QPixmap(Theme::hidpiFileName(":/client/theme/colored/wizard-nextcloud.png"));
+    }
+#ifdef APPLICATION_WIZARD_USE_CUSTOM_LOGO
+    const auto useSvg = shouldPreferSvg();
+    const auto logoBasePath = QStringLiteral(":/client/theme/colored/wizard_logo");
+    if (useSvg) {
+        const auto maxHeight = Theme::isHidpi() ? 200 : 100;
+        const auto maxWidth = 2 * maxHeight;
+        const auto icon = QIcon(logoBasePath + ".svg");
+        const auto size = icon.actualSize(QSize(maxWidth, maxHeight));
+        return icon.pixmap(size);
+    } else {
+        return QPixmap(hidpiFileName(logoBasePath + ".png"));
+    }
+#else
+    const auto size = Theme::isHidpi() ?: 200 : 100;
+    return applicationIcon().pixmap(size);
+#endif
+}
+
 QPixmap Theme::wizardHeaderLogo() const
 {
 #ifdef APPLICATION_WIZARD_USE_CUSTOM_LOGO

+ 6 - 0
src/libsync/theme.h

@@ -132,6 +132,10 @@ public:
 #ifndef TOKEN_AUTH_ONLY
     static QString hidpiFileName(const QString &fileName, QPaintDevice *dev = nullptr);
 
+    static QString hidpiFileName(const QString &iconName, const QColor &backgroundColor, QPaintDevice *dev = nullptr);
+
+    static bool isHidpi(QPaintDevice *dev = nullptr);
+
     /**
       * get an sync state icon
       */
@@ -231,6 +235,8 @@ public:
     /** @return color for the setup wizard. */
     virtual QColor wizardHeaderBackgroundColor() const;
 
+    virtual QPixmap wizardApplicationLogo() const;
+
     /** @return logo for the setup wizard. */
     virtual QPixmap wizardHeaderLogo() const;
 

+ 2 - 0
test/CMakeLists.txt

@@ -9,6 +9,7 @@ add_library(testutils
   STATIC
   syncenginetestutils.cpp
   pushnotificationstestutils.cpp
+  themeutils.cpp
 )
 
 target_link_libraries(testutils PUBLIC ${APPLICATION_EXECUTABLE}sync Qt5::Test)
@@ -54,6 +55,7 @@ nextcloud_add_test(LockedFiles)
 nextcloud_add_test(FolderWatcher)
 nextcloud_add_test(Capabilities)
 nextcloud_add_test(PushNotifications)
+nextcloud_add_test(Theme)
 
 if( UNIX AND NOT APPLE )
     nextcloud_add_test(InotifyWatcher)

+ 111 - 0
test/testtheme.cpp

@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2021 by Felix Weilbach <felix.weilbach@nextcloud.com>
+ *
+ * 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.
+ */
+
+#include <QTest>
+
+#include "theme.h"
+#include "themeutils.h"
+
+class TestTheme : public QObject
+{
+    Q_OBJECT
+
+public:
+    TestTheme()
+    {
+        Q_INIT_RESOURCE(resources);
+        Q_INIT_RESOURCE(theme);
+    }
+
+private slots:
+    void testHidpiFileName_darkBackground_returnPathToWhiteIcon()
+    {
+        FakePaintDevice paintDevice;
+        const QColor backgroundColor("#000000");
+        const QString iconName("icon-name");
+
+        const auto iconPath = OCC::Theme::hidpiFileName(iconName + ".png", backgroundColor, &paintDevice);
+
+        QCOMPARE(iconPath, ":/client/theme/white/" + iconName + ".png");
+    }
+
+    void testHidpiFileName_lightBackground_returnPathToBlackIcon()
+    {
+        FakePaintDevice paintDevice;
+        const QColor backgroundColor("#ffffff");
+        const QString iconName("icon-name");
+
+        const auto iconPath = OCC::Theme::hidpiFileName(iconName + ".png", backgroundColor, &paintDevice);
+
+        QCOMPARE(iconPath, ":/client/theme/black/" + iconName + ".png");
+    }
+
+    void testHidpiFileName_hidpiDevice_returnHidpiIconPath()
+    {
+        FakePaintDevice paintDevice;
+        paintDevice.setHidpi(true);
+        const QColor backgroundColor("#000000");
+        const QString iconName("wizard-files");
+
+        const auto iconPath = OCC::Theme::hidpiFileName(iconName + ".png", backgroundColor, &paintDevice);
+
+        QCOMPARE(iconPath, ":/client/theme/white/" + iconName + "@2x.png");
+    }
+
+    void testIsDarkColor_nextcloudBlue_returnTrue()
+    {
+        const QColor color(0, 130, 201);
+
+        const auto result = OCC::Theme::isDarkColor(color);
+
+        QCOMPARE(result, true);
+    }
+
+    void testIsDarkColor_lightColor_returnFalse()
+    {
+        const QColor color(255, 255, 255);
+
+        const auto result = OCC::Theme::isDarkColor(color);
+
+        QCOMPARE(result, false);
+    }
+
+    void testIsDarkColor_darkColor_returnTrue()
+    {
+        const QColor color(0, 0, 0);
+
+        const auto result = OCC::Theme::isDarkColor(color);
+
+        QCOMPARE(result, true);
+    }
+
+    void testIsHidpi_hidpi_returnTrue()
+    {
+        FakePaintDevice paintDevice;
+        paintDevice.setHidpi(true);
+
+        QCOMPARE(OCC::Theme::isHidpi(&paintDevice), true);
+    }
+
+    void testIsHidpi_lowdpi_returnFalse()
+    {
+        FakePaintDevice paintDevice;
+        paintDevice.setHidpi(false);
+
+        QCOMPARE(OCC::Theme::isHidpi(&paintDevice), false);
+    }
+};
+
+QTEST_GUILESS_MAIN(TestTheme)
+#include "testtheme.moc"

+ 40 - 0
test/themeutils.cpp

@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 by Felix Weilbach <felix.weilbach@nextcloud.com>
+ *
+ * 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.
+ */
+
+#include "themeutils.h"
+
+FakePaintDevice::FakePaintDevice() = default;
+
+QPaintEngine *FakePaintDevice::paintEngine() const
+{
+    return nullptr;
+}
+
+void FakePaintDevice::setHidpi(bool value)
+{
+    _hidpi = value;
+}
+
+int FakePaintDevice::metric(QPaintDevice::PaintDeviceMetric metric) const
+{
+    switch (metric) {
+    case QPaintDevice::PdmDevicePixelRatio:
+        if (_hidpi) {
+            return 2;
+        }
+        return 1;
+    default:
+        return QPaintDevice::metric(metric);
+    }
+}

+ 34 - 0
test/themeutils.h

@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) by Felix Weilbach <felix.weilbach@nextcloud.com>
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <QPaintDevice>
+#include <QTest>
+
+class FakePaintDevice : public QPaintDevice
+{
+public:
+    FakePaintDevice();
+
+    QPaintEngine *paintEngine() const override;
+
+    void setHidpi(bool value);
+
+protected:
+    int metric(QPaintDevice::PaintDeviceMetric metric) const override;
+
+private:
+    bool _hidpi = false;
+};