File indexing completed on 2024-05-19 04:29:07

0001 /*
0002  *  SPDX-FileCopyrightText: 2014 Boudewijn Rempt <boud@valdyas.org>
0003  *  SPDX-FileCopyrightText: 2021 Alvin Wong <alvin@alvinhc.com>
0004  *
0005  *  SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 #include "kis_splash_screen.h"
0008 
0009 #include <QApplication>
0010 #include <QDesktopWidget>
0011 #include <QGraphicsDropShadowEffect>
0012 #include <QPixmap>
0013 #include <QPainter>
0014 #include <QCheckBox>
0015 #include <kis_debug.h>
0016 #include <QFile>
0017 #include <QScreen>
0018 #include <QWindow>
0019 #include <QSvgWidget>
0020 
0021 #include <KisPart.h>
0022 #include <KisApplication.h>
0023 
0024 #include <kis_icon.h>
0025 
0026 #include <klocalizedstring.h>
0027 #include <kconfig.h>
0028 #include <ksharedconfig.h>
0029 #include <kconfiggroup.h>
0030 #include <QIcon>
0031 
0032 static void addDropShadow(QWidget *widget)
0033 {
0034     QGraphicsDropShadowEffect *effect = new QGraphicsDropShadowEffect(widget);
0035     effect->setBlurRadius(4);
0036     effect->setOffset(0.5);
0037     effect->setColor(QColor(0, 0, 0, 255));
0038     widget->setGraphicsEffect(effect);
0039 }
0040 
0041 KisSplashScreen::KisSplashScreen(bool themed, QWidget *parent, Qt::WindowFlags f)
0042     : QWidget(parent, Qt::SplashScreen | Qt::FramelessWindowHint | f)
0043       , m_themed(themed)
0044       , m_versionHtml(qApp->applicationVersion().toHtmlEscaped())
0045 {
0046 
0047     setupUi(this);
0048     setWindowIcon(KisIconUtils::loadIcon("krita-branding"));
0049 
0050     m_loadingTextLabel = new QLabel(lblSplash);
0051     m_loadingTextLabel->setTextFormat(Qt::RichText);
0052     m_loadingTextLabel->setStyleSheet(QStringLiteral("QLabel { color: #fff; background-color: transparent; }"));
0053     m_loadingTextLabel->setAlignment(Qt::AlignRight | Qt::AlignTop);
0054     addDropShadow(m_loadingTextLabel);
0055 
0056     m_brandingSvg = new QSvgWidget(QStringLiteral(":/krita-branding.svgz"), lblSplash);
0057     m_bannerSvg = new QSvgWidget(QStringLiteral(":/splash/banner.svg"), lblSplash);
0058     addDropShadow(m_bannerSvg);
0059 
0060     m_artCreditsLabel = new QLabel(lblSplash);
0061     m_artCreditsLabel->setTextFormat(Qt::PlainText);
0062     m_artCreditsLabel->setStyleSheet(QStringLiteral("QLabel { color: #fff; background-color: transparent; font: 10pt; }"));
0063     m_artCreditsLabel->setAlignment(Qt::AlignRight | Qt::AlignBottom);
0064     addDropShadow(m_artCreditsLabel);
0065 
0066     updateSplashImage();
0067     setLoadingText(QString());
0068 
0069     bnClose->hide();
0070     connect(bnClose, SIGNAL(clicked()), this, SLOT(close()));
0071     chkShowAtStartup->hide();
0072     connect(chkShowAtStartup, SIGNAL(toggled(bool)), this, SLOT(toggleShowAtStartup(bool)));
0073 
0074     KConfigGroup cfg( KSharedConfig::openConfig(), "SplashScreen");
0075     bool hideSplash = cfg.readEntry("HideSplashAfterStartup", false);
0076     chkShowAtStartup->setChecked(hideSplash);
0077 
0078     connect(lblRecent, SIGNAL(linkActivated(QString)), SLOT(linkClicked(QString)));
0079     connect(&m_timer, SIGNAL(timeout()), SLOT(raise()));
0080 
0081     // hide these labels by default
0082     displayLinks(false);
0083     displayRecentFiles(false);
0084 
0085     m_timer.setSingleShot(true);
0086     m_timer.start(10);
0087 }
0088 
0089 void KisSplashScreen::updateSplashImage()
0090 {
0091     constexpr int SPLASH_HEIGHT_LOADING = 480;
0092     constexpr int SPLASH_HEIGHT_ABOUT = 320;
0093 
0094     int splashHeight;
0095     if (m_displayLinks) {
0096         splashHeight = SPLASH_HEIGHT_ABOUT;
0097     } else {
0098         splashHeight = SPLASH_HEIGHT_LOADING;
0099     }
0100     const int bannerHeight = splashHeight * 0.16875;
0101     const int marginTop = splashHeight * 0.05;
0102     const int marginRight = splashHeight * 0.1;
0103 
0104     QString splashName = QStringLiteral(":/splash/0.png");
0105     QString splashArtist = QStringLiteral("Tyson Tan");
0106     // TODO: Re-add the holiday splash...
0107 #if 0
0108     QDate currentDate = QDate::currentDate();
0109     if (currentDate > QDate(currentDate.year(), 12, 4) ||
0110             currentDate < QDate(currentDate.year(), 1, 9)) {
0111         splashName = QStringLiteral(":/splash/1.png");
0112         splashArtist = QStringLiteral("???");
0113     }
0114 #endif
0115 
0116     QPixmap img(splashName);
0117 
0118     // Preserve aspect ratio of splash.
0119     const int height = splashHeight;
0120     const int width = height * img.width() / img.height();
0121 
0122     setFixedWidth(width);
0123     setFixedHeight(height);
0124     lblSplash->setFixedWidth(width);
0125     lblSplash->setFixedHeight(height);
0126 
0127     // Get a downscaled pixmap of the splash.
0128     const int pixelWidth = width * devicePixelRatioF();
0129     const int pixelHeight = height * devicePixelRatioF();
0130     img = img.scaled(pixelWidth, pixelHeight, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
0131     img.setDevicePixelRatio(devicePixelRatioF());
0132     lblSplash->setPixmap(img);
0133 
0134     // Align banner to top-left with margin.
0135     m_bannerSvg->setFixedHeight(bannerHeight);
0136     m_bannerSvg->setFixedWidth(bannerHeight * m_bannerSvg->sizeHint().width() / m_bannerSvg->sizeHint().height());
0137     m_bannerSvg->move(width - m_bannerSvg->width() - marginRight, marginTop);
0138 
0139     // Place logo to the left of banner.
0140     m_brandingSvg->setFixedSize(bannerHeight, bannerHeight);
0141     m_brandingSvg->move(m_bannerSvg->x() - m_brandingSvg->width(), marginTop);
0142 
0143     // Place loading text immediately below.
0144     m_loadingTextLabel->move(marginRight, m_brandingSvg->geometry().bottom());
0145     m_loadingTextLabel->setFixedWidth(m_bannerSvg->geometry().right() - marginRight);
0146 
0147     // Place credits text on bottom right with similar margins.
0148     if (splashArtist.isEmpty()) {
0149         m_artCreditsLabel->setText(QString());
0150     } else {
0151         m_artCreditsLabel->setText(i18nc("splash image credit", "Artwork by: %1", splashArtist));
0152     }
0153     m_artCreditsLabel->setFixedWidth(m_loadingTextLabel->width());
0154     m_artCreditsLabel->setFixedHeight(20);
0155     m_artCreditsLabel->move(m_loadingTextLabel->x(), height - marginTop - m_artCreditsLabel->height());
0156 
0157     if (m_displayLinks) {
0158         setFixedSize(sizeHint());
0159     }
0160 }
0161 
0162 void KisSplashScreen::resizeEvent(QResizeEvent *event)
0163 {
0164     QWidget::resizeEvent(event);
0165     updateText();
0166 }
0167 
0168 void KisSplashScreen::updateText()
0169 {
0170     QString color = colorString();
0171 
0172     KConfigGroup cfg2( KSharedConfig::openConfig(), "RecentFiles");
0173     int i = 1;
0174 
0175     QString recent = i18n("<html>"
0176                           "<head/>"
0177                           "<body>"
0178                           "<p><b><span style=\" color:%1;\">Recent Files</span></b></p>", color);
0179 
0180     QString path;
0181     QStringList recentfiles;
0182 
0183     QFontMetrics metrics(lblRecent->font());
0184 
0185     do {
0186         path = cfg2.readPathEntry(QString("File%1").arg(i), QString());
0187         if (!path.isEmpty()) {
0188             QString name = cfg2.readPathEntry(QString("Name%1").arg(i), QString());
0189             QUrl url(path);
0190             if (name.isEmpty()) {
0191                 name = url.fileName();
0192             }
0193 
0194             name = metrics.elidedText(name, Qt::ElideMiddle, lblRecent->width());
0195 
0196             if (!url.isLocalFile() || QFile::exists(url.toLocalFile())) {
0197                 recentfiles.insert(0, QString("<p><a href=\"%1\"><span style=\"color:%3;\">%2</span></a></p>").arg(path).arg(name).arg(color));
0198             }
0199         }
0200 
0201         i++;
0202     } while (!path.isEmpty() || i <= 8);
0203 
0204     recent += recentfiles.join("\n");
0205     recent += "</body>"
0206         "</html>";
0207     lblRecent->setText(recent);
0208 }
0209 
0210 void KisSplashScreen::displayLinks(bool show) {
0211 
0212     if (show) {
0213         QString color = colorString();
0214         lblLinks->setTextFormat(Qt::RichText);
0215         lblLinks->setText(i18n("<html>"
0216                                "<head/>"
0217                                "<body><table width=\"100%\" cellpadding=\"30\"><tr><td>"
0218                                "<p><span style=\" color:%1;\"><b>Using Krita</b></span></p>"
0219                                "<p><a href=\"https://krita.org/support-us/\"><span style=\" text-decoration: underline; color:%1;\">Support Krita's Development!</span></a></p>"
0220                                "<p><a href=\"https://docs.krita.org/en/user_manual/getting_started.html\"><span style=\" text-decoration: underline; color:%1;\">Getting Started</span></a></p>"
0221                                "<p><a href=\"https://docs.krita.org/\"><span style=\" text-decoration: underline; color:%1;\">Manual</span></a></p>"
0222                                "<p><a href=\"https://krita.org/\"><span style=\" text-decoration: underline; color:%1;\">Krita Website</span></a></p>"
0223                                "</td><td><p><span style=\" color:%1;\"><b>Coding Krita</b></span></p>"
0224                                "<p><a href=\"https://krita-artists.org\"><span style=\" text-decoration: underline; color:%1;\">User Community</span></a></p>"
0225                                "<p><a href=\"https://invent.kde.org/graphics/krita\"><span style=\" text-decoration: underline; color:%1;\">Source Code</span></a></p>"
0226                                "<p><a href=\"https://api.kde.org/krita/html/classKrita.html\"><span style=\" text-decoration: underline; color:%1;\">Scripting API</span></a></p>"
0227                                "<p><a href=\"https://scripting.krita.org/lessons/introduction\"><span style=\" text-decoration: underline; color:%1;\">Scripting School</span></a></p>"
0228                                "</td></table></body>"
0229                                "</html>", color));
0230 
0231         filesLayout->setContentsMargins(10,10,10,10);
0232         actionControlsLayout->setContentsMargins(5,5,5,5);
0233 
0234     } else {
0235         // eliminating margins here allows for the splash screen image to take the entire area with nothing underneath
0236         filesLayout->setContentsMargins(0,0,0,0);
0237         actionControlsLayout->setContentsMargins(0,0,0,0);
0238     }
0239 
0240     lblLinks->setVisible(show);
0241 
0242     updateText();
0243 
0244     if (m_displayLinks != show) {
0245         m_displayLinks = show;
0246         updateSplashImage();
0247     }
0248 }
0249 
0250 
0251 void KisSplashScreen::displayRecentFiles(bool show) {
0252     lblRecent->setVisible(show);
0253     line->setVisible(show);
0254 }
0255 
0256 void KisSplashScreen::setLoadingText(QString text)
0257 {
0258     int larger = 12;
0259     int notAsLarge = larger - 1;
0260     QString htmlText = QStringLiteral("<span style='font: %3pt;'><span style='font: bold %4pt;'>%1</span><br><i>%2</i></span>")
0261             .arg(m_versionHtml, text.toHtmlEscaped(), QString::number(notAsLarge), QString::number(larger));
0262     m_loadingTextLabel->setText(htmlText);
0263 }
0264 
0265 
0266 
0267 QString KisSplashScreen::colorString() const
0268 {
0269     QString color = "#FFFFFF";
0270     if (m_themed && qApp->palette().window().color().value() > 100) {
0271         color = "#000000";
0272     }
0273 
0274     return color;
0275 }
0276 
0277 
0278 void KisSplashScreen::repaint()
0279 {
0280     QWidget::repaint();
0281     qApp->sendPostedEvents();
0282 }
0283 
0284 void KisSplashScreen::show()
0285 {
0286     if (!this->parentWidget()) {
0287         this->winId(); // Force creation of native window
0288         QWindow *windowHandle = this->windowHandle();
0289         QScreen *screen = windowHandle ? windowHandle->screen() : nullptr;
0290         if (windowHandle && !screen) {
0291             // At least on Windows, the window may be created on a non-primary
0292             // screen with a different scale factor. If we don't explicitly
0293             // move it to the primary screen, the position will be scaled with
0294             // the wrong factor and the splash will be offset.
0295             // XXX: In theory this branch should be unreachable, but leaving
0296             //      this here just in case.
0297             windowHandle->setScreen(QApplication::primaryScreen());
0298         }
0299         if (!screen) {
0300             screen = QApplication::primaryScreen();
0301         }
0302         // Reinitialize the splash image as the screen may have a different
0303         // devicePixelRatio.
0304         updateSplashImage();
0305         QRect r(QPoint(), size());
0306         move(screen->availableGeometry().center() - r.center());
0307     }
0308     if (isVisible()) {
0309         repaint();
0310     }
0311     m_timer.setSingleShot(true);
0312     m_timer.start(1);
0313     QWidget::show();
0314 }
0315 
0316 void KisSplashScreen::toggleShowAtStartup(bool toggle)
0317 {
0318     KConfigGroup cfg( KSharedConfig::openConfig(), "SplashScreen");
0319     cfg.writeEntry("HideSplashAfterStartup", toggle);
0320 }
0321 
0322 void KisSplashScreen::linkClicked(const QString &link)
0323 {
0324     KisPart::instance()->openExistingFile(link);
0325     if (isTopLevel()) {
0326         close();
0327     }
0328 }