diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 4684e9117..e626ebce4 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -116,6 +116,7 @@ set(client_SRCS wizard/owncloudwizardresultpage.cpp wizard/webviewpage.cpp wizard/webview.cpp + wizard/slideshow.cpp ) IF(NOT NO_SHIBBOLETH) diff --git a/src/gui/wizard/owncloudsetupnocredspage.ui b/src/gui/wizard/owncloudsetupnocredspage.ui index facd616f6..e28271815 100644 --- a/src/gui/wizard/owncloudsetupnocredspage.ui +++ b/src/gui/wizard/owncloudsetupnocredspage.ui @@ -190,28 +190,6 @@ </item> </layout> </item> - <item row="1" column="0"> - <widget class="QLabel" name="slideImage"> - <property name="minimumSize"> - <size> - <width>0</width> - <height>100</height> - </size> - </property> - <property name="text"> - <string/> - </property> - <property name="pixmap"> - <pixmap resource="../../../theme.qrc">:/client/theme/colored/wizard-files.svg</pixmap> - </property> - <property name="scaledContents"> - <bool>false</bool> - </property> - <property name="alignment"> - <set>Qt::AlignCenter</set> - </property> - </widget> - </item> <item row="6" column="0"> <spacer name="verticalSpacer_4"> <property name="orientation"> @@ -229,7 +207,7 @@ </spacer> </item> <item row="2" column="0"> - <widget class="QLabel" name="slideLabel"> + <widget class="OCC::SlideShow" name="slideShow"> <property name="font"> <font> <pointsize>12</pointsize> @@ -237,12 +215,6 @@ <bold>true</bold> </font> </property> - <property name="text"> - <string>SlideshowLabel</string> - </property> - <property name="alignment"> - <set>Qt::AlignCenter</set> - </property> </widget> </item> <item row="4" column="0"> @@ -437,6 +409,11 @@ <extends>QLineEdit</extends> <header>wizard/postfixlineedit.h</header> </customwidget> + <customwidget> + <class>OCC::SlideShow</class> + <extends>QWidget</extends> + <header>wizard/slideshow.h</header> + </customwidget> </customwidgets> <resources> <include location="../../../theme.qrc"/> diff --git a/src/gui/wizard/owncloudsetuppage.cpp b/src/gui/wizard/owncloudsetuppage.cpp index eb5291a77..994f65bf9 100644 --- a/src/gui/wizard/owncloudsetuppage.cpp +++ b/src/gui/wizard/owncloudsetuppage.cpp @@ -31,6 +31,7 @@ #include "wizard/owncloudwizardcommon.h" #include "wizard/owncloudsetuppage.h" #include "wizard/owncloudconnectionmethoddialog.h" +#include "wizard/slideshow.h" #include "theme.h" #include "account.h" #include "config.h" @@ -80,18 +81,16 @@ OwncloudSetupPage::OwncloudSetupPage(QWidget *parent) connect(_ui.createAccountButton, &QPushButton::clicked, this, &OwncloudSetupPage::slotGotoProviderList); _ui.login->hide(); - _slideshow.append(qMakePair(QString("nextcloud"), tr("Keep your data secure and under your control"))); - _slideshow.append(qMakePair(QString("files"), tr("Secure collaboration & file exchange"))); - _slideshow.append(qMakePair(QString("groupware"), tr("Easy-to-use web mail, calendaring & contacts"))); - _slideshow.append(qMakePair(QString("talk"), tr("Screensharing, online meetings & web conferences"))); + _ui.slideShow->addSlide(Theme::hidpiFileName(":/client/theme/colored/wizard-nextcloud.png"), tr("Keep your data secure and under your control")); + _ui.slideShow->addSlide(Theme::hidpiFileName(":/client/theme/colored/wizard-files.png"), tr("Secure collaboration & file exchange")); + _ui.slideShow->addSlide(Theme::hidpiFileName(":/client/theme/colored/wizard-groupware.png"), tr("Easy-to-use web mail, calendaring & contacts")); + _ui.slideShow->addSlide(Theme::hidpiFileName(":/client/theme/colored/wizard-talk.png"), tr("Screensharing, online meetings & web conferences")); + connect(_ui.slideShow, &SlideShow::clicked, _ui.slideShow, &SlideShow::nextSlide); + _ui.slideShow->startShow(2500); - _ui.slideLabel->setStyleSheet(QString("color:%1;").arg(theme->wizardHeaderBackgroundColor().name())); - _currentSlide = -1; - nextSlide(); - - QTimer *timer = new QTimer(this); - connect(timer, SIGNAL(timeout()), this, SLOT(nextSlide())); - timer->start(2500); + QPalette pal = _ui.slideShow->palette(); + pal.setColor(QPalette::WindowText, theme->wizardHeaderBackgroundColor()); + _ui.slideShow->setPalette(pal); #else _ui.createAccountButton->hide(); _ui.slideImage->hide(); @@ -102,20 +101,6 @@ OwncloudSetupPage::OwncloudSetupPage(QWidget *parent) setStyleSheet(QString("background-color:%1; color:%2 QLabel { color:%2; } QSpacerItem { color: red; }").arg(theme->wizardHeaderBackgroundColor().name(), theme->wizardHeaderTitleColor().name())); } -#ifdef WITH_PROVIDERS -void OwncloudSetupPage::nextSlide() -{ - if (_currentSlide < _slideshow.length() - 1) { - _currentSlide++; - } else { - _currentSlide = 0; - } - QPixmap pixmap = QIcon(Theme::hidpiFileName(":/client/theme/colored/wizard-" + _slideshow.at(_currentSlide).first + ".svg")).pixmap(QSize(1024, 1024)); - _ui.slideImage->setPixmap(pixmap.scaled(QSize(_ui.slideImage->size().height(), _ui.slideImage->size().height()), Qt::KeepAspectRatio, Qt::SmoothTransformation)); - _ui.slideLabel->setText(_slideshow.at(_currentSlide).second); -} -#endif - void OwncloudSetupPage::setServerUrl(const QString &newUrl) { _ocWizard->setRegistration(false); diff --git a/src/gui/wizard/owncloudsetuppage.h b/src/gui/wizard/owncloudsetuppage.h index 3b0772f07..b8893d271 100644 --- a/src/gui/wizard/owncloudsetuppage.h +++ b/src/gui/wizard/owncloudsetuppage.h @@ -62,9 +62,6 @@ public slots: void startSpinner(); void stopSpinner(); void slotCertificateAccepted(); -#ifdef WITH_PROVIDERS - void nextSlide(); -#endif protected slots: void slotUrlChanged(const QString &); @@ -96,10 +93,6 @@ private: QString _remoteFolder; AddCertificateDialog *addCertDial; OwncloudWizard *_ocWizard; - - QList<QPair<QString, QString>> _slideshow; - int _currentSlide; - }; } // namespace OCC diff --git a/src/gui/wizard/slideshow.cpp b/src/gui/wizard/slideshow.cpp new file mode 100644 index 000000000..37b4a3f3f --- /dev/null +++ b/src/gui/wizard/slideshow.cpp @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2018 by J-P Nurmi <jpnurmi@gmail.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 "slideshow.h" +#include <QGuiApplication> +#include <QMouseEvent> +#include <QPainter> +#include <QStyle> +#include <QStyleHints> + +namespace OCC { + +static const int Spacing = 6; +static const int SlideDuration = 250; +static const int SlideDistance = 200; + +SlideShow::SlideShow(QWidget *parent) : QWidget(parent) +{ + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); +} + +void SlideShow::addSlide(const QPixmap &pixmap, const QString &label) +{ + _labels += label; + _pixmaps += pixmap; + updateGeometry(); +} + +bool SlideShow::isActive() const +{ + return _timer.isActive(); +} + +int SlideShow::interval() const +{ + return _interval; +} + +void SlideShow::setInterval(int interval) +{ + if (_interval == interval) + return; + + _interval = interval; + maybeRestartTimer(); +} + +int SlideShow::currentSlide() const +{ + return _currentIndex; +} + +void SlideShow::setCurrentSlide(int index) +{ + if (_currentIndex == index) + return; + + if (!_animation) { + _animation = new QVariantAnimation(this); + _animation->setDuration(SlideDuration); + _animation->setEasingCurve(QEasingCurve::OutCubic); + _animation->setStartValue(static_cast<qreal>(_currentIndex)); + connect(_animation.data(), SIGNAL(valueChanged(QVariant)), this, SLOT(update())); + } + _animation->setEndValue(static_cast<qreal>(index)); + _animation->start(QAbstractAnimation::DeleteWhenStopped); + + _reverse = index < _currentIndex; + _currentIndex = index; + maybeRestartTimer(); + update(); + emit currentSlideChanged(index); +} + +QSize SlideShow::sizeHint() const +{ + QFontMetrics fm = fontMetrics(); + QSize labelSize(0, fm.height()); + for (const QString &label : _labels) { + labelSize.setWidth(std::max(fm.width(label), labelSize.width())); + } + QSize pixmapSize; + for (const QPixmap &pixmap : _pixmaps) { + pixmapSize.setWidth(std::max(pixmap.width(), pixmapSize.width())); + pixmapSize.setHeight(std::max(pixmap.height(), pixmapSize.height())); + } + return QSize(std::max(labelSize.width(), pixmapSize.width()), labelSize.height() + Spacing + pixmapSize.height()); +} + +void SlideShow::startShow(int interval) +{ + if (interval > 0) + _interval = interval; + _timer.start(_interval, this); +} + +void SlideShow::stopShow() +{ + _timer.stop(); +} + +void SlideShow::nextSlide() +{ + setCurrentSlide((_currentIndex + 1) % _labels.count()); + _reverse = false; +} + +void SlideShow::previousSlide() +{ + setCurrentSlide((_currentIndex > 0 ? _currentIndex : _labels.count()) - 1); + _reverse = true; +} + +void SlideShow::reset() +{ + stopShow(); + _pixmaps.clear(); + _labels.clear(); + updateGeometry(); + update(); +} + +void SlideShow::mousePressEvent(QMouseEvent *event) +{ + _pressPoint = event->pos(); +} + +void SlideShow::mouseReleaseEvent(QMouseEvent *event) +{ + if (QLineF(_pressPoint, event->pos()).length() < QGuiApplication::styleHints()->startDragDistance()) + emit clicked(); +} + +void SlideShow::paintEvent(QPaintEvent *) +{ + QPainter painter(this); + + if (_animation) { + int from = _animation->startValue().toInt(); + int to = _animation->endValue().toInt(); + qreal progress = _animation->easingCurve().valueForProgress(_animation->currentTime() / static_cast<qreal>(_animation->duration())); + + painter.save(); + painter.setOpacity(1.0 - progress); + painter.translate(progress * (_reverse ? SlideDistance : -SlideDistance), 0); + drawSlide(&painter, from); + + painter.restore(); + painter.setOpacity(progress); + painter.translate((1.0 - progress) * (_reverse ? -SlideDistance : SlideDistance), 0); + drawSlide(&painter, to); + } else { + drawSlide(&painter, _currentIndex); + } +} + +void SlideShow::timerEvent(QTimerEvent *event) +{ + if (event->timerId() == _timer.timerId()) + nextSlide(); +} + +void SlideShow::maybeRestartTimer() +{ + if (!isActive()) + return; + + startShow(); +} + +void SlideShow::drawSlide(QPainter *painter, int index) +{ + QString label = _labels.value(index); + QRect labelRect = style()->itemTextRect(fontMetrics(), rect(), Qt::AlignBottom | Qt::AlignHCenter, isEnabled(), label); + style()->drawItemText(painter, labelRect, Qt::AlignCenter, palette(), isEnabled(), label, QPalette::WindowText); + + QPixmap pixmap = _pixmaps.value(index); + QRect pixmapRect = style()->itemPixmapRect(QRect(0, 0, width(), labelRect.top() - Spacing), Qt::AlignCenter, pixmap); + style()->drawItemPixmap(painter, pixmapRect, Qt::AlignCenter, pixmap); +} + +} // namespace OCC diff --git a/src/gui/wizard/slideshow.h b/src/gui/wizard/slideshow.h new file mode 100644 index 000000000..e00236a40 --- /dev/null +++ b/src/gui/wizard/slideshow.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2018 by J-P Nurmi <jpnurmi@gmail.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. + */ + +#ifndef OCC_SLIDESHOW_H +#define OCC_SLIDESHOW_H + +#include <QWidget> +#include <QBasicTimer> +#include <QPointer> +#include <QVariantAnimation> + +namespace OCC { + +/** + * @brief The SlideShow class + * @ingroup gui + */ +class SlideShow : public QWidget +{ + Q_OBJECT + Q_PROPERTY(int interval READ interval WRITE setInterval) + Q_PROPERTY(int currentSlide READ currentSlide WRITE setCurrentSlide NOTIFY currentSlideChanged) + +public: + explicit SlideShow(QWidget* parent = nullptr); + + void addSlide(const QPixmap &pixmap, const QString &label); + + bool isActive() const; + + int interval() const; + void setInterval(int interval); + + int currentSlide() const; + void setCurrentSlide(int index); + + QSize sizeHint() const override; + +public slots: + void startShow(int interval = 0); + void stopShow(); + void nextSlide(); + void previousSlide(); + void reset(); + +signals: + void clicked(); + void currentSlideChanged(int index); + +protected: + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void paintEvent(QPaintEvent *event); + void timerEvent(QTimerEvent *event); + +private: + void maybeRestartTimer(); + void drawSlide(QPainter *painter, int index); + + bool _reverse = false; + int _interval = 2500; + int _currentIndex = 0; + QPoint _pressPoint; + QBasicTimer _timer; + QStringList _labels; + QVector<QPixmap> _pixmaps; + QPointer<QVariantAnimation> _animation = nullptr; +}; + +} // namespace OCC + +#endif // OCC_SLIDESHOW_H diff --git a/theme.qrc b/theme.qrc index 630400835..11641c386 100644 --- a/theme.qrc +++ b/theme.qrc @@ -117,9 +117,13 @@ <file>theme/white/state-warning-64.png</file> <file>theme/white/state-warning-128.png</file> <file>theme/white/state-warning-256.png</file> - <file>theme/colored/wizard-files.svg</file> - <file>theme/colored/wizard-groupware.svg</file> - <file>theme/colored/wizard-nextcloud.svg</file> - <file>theme/colored/wizard-talk.svg</file> + <file>theme/colored/wizard-files.png</file> + <file>theme/colored/wizard-files@2x.png</file> + <file>theme/colored/wizard-groupware.png</file> + <file>theme/colored/wizard-groupware@2x.png</file> + <file>theme/colored/wizard-nextcloud.png</file> + <file>theme/colored/wizard-nextcloud@2x.png</file> + <file>theme/colored/wizard-talk.png</file> + <file>theme/colored/wizard-talk@2x.png</file> </qresource> </RCC> diff --git a/theme/colored/wizard-files.png b/theme/colored/wizard-files.png new file mode 100644 index 000000000..236bfa773 Binary files /dev/null and b/theme/colored/wizard-files.png differ diff --git a/theme/colored/wizard-files@2x.png b/theme/colored/wizard-files@2x.png new file mode 100644 index 000000000..4f1eb74ac Binary files /dev/null and b/theme/colored/wizard-files@2x.png differ diff --git a/theme/colored/wizard-groupware.png b/theme/colored/wizard-groupware.png new file mode 100644 index 000000000..417629bb9 Binary files /dev/null and b/theme/colored/wizard-groupware.png differ diff --git a/theme/colored/wizard-groupware@2x.png b/theme/colored/wizard-groupware@2x.png new file mode 100644 index 000000000..cea41d3c6 Binary files /dev/null and b/theme/colored/wizard-groupware@2x.png differ diff --git a/theme/colored/wizard-nextcloud.png b/theme/colored/wizard-nextcloud.png new file mode 100644 index 000000000..321f83e31 Binary files /dev/null and b/theme/colored/wizard-nextcloud.png differ diff --git a/theme/colored/wizard-nextcloud@2x.png b/theme/colored/wizard-nextcloud@2x.png new file mode 100644 index 000000000..a73e467d8 Binary files /dev/null and b/theme/colored/wizard-nextcloud@2x.png differ diff --git a/theme/colored/wizard-talk.png b/theme/colored/wizard-talk.png new file mode 100644 index 000000000..e1c3839a1 Binary files /dev/null and b/theme/colored/wizard-talk.png differ diff --git a/theme/colored/wizard-talk@2x.png b/theme/colored/wizard-talk@2x.png new file mode 100644 index 000000000..92a368062 Binary files /dev/null and b/theme/colored/wizard-talk@2x.png differ