Merge pull request #16438 from 3x380V/dlgabout
Gui: refactor splash screen and about dialog
This commit is contained in:
@@ -2250,11 +2250,8 @@ void Application::runApplication()
|
||||
// A new QApplication
|
||||
Base::Console().Log("Init: Creating Gui::Application and QApplication\n");
|
||||
|
||||
// if application not yet created by the splasher
|
||||
int argc = App::Application::GetARGC();
|
||||
GUISingleApplication mainApp(argc, App::Application::GetARGV());
|
||||
// https://forum.freecad.org/viewtopic.php?f=3&t=15540
|
||||
QApplication::setAttribute(Qt::AA_DontShowIconsInMenus, false);
|
||||
|
||||
// Make sure that we use '.' as decimal point. See also
|
||||
// http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=559846
|
||||
@@ -2304,6 +2301,14 @@ void Application::runApplication()
|
||||
Base::Console().Log("Finish: Event loop left\n");
|
||||
}
|
||||
|
||||
bool Application::hiddenMainWindow()
|
||||
{
|
||||
const std::map<std::string,std::string>& cfg = App::Application::Config();
|
||||
auto it = cfg.find("StartHidden");
|
||||
|
||||
return it != cfg.end();
|
||||
}
|
||||
|
||||
bool Application::testStatus(Status pos) const
|
||||
{
|
||||
return d->StatusBits.test((size_t)pos);
|
||||
|
||||
@@ -247,6 +247,8 @@ public:
|
||||
void tryClose( QCloseEvent * e );
|
||||
//@}
|
||||
|
||||
/// whenever GUI is about to start with the main window hidden
|
||||
static bool hiddenMainWindow();
|
||||
/// return the status bits
|
||||
bool testStatus(Status pos) const;
|
||||
/// set the status bits
|
||||
|
||||
@@ -444,6 +444,7 @@ SOURCE_GROUP("Command" FILES ${Command_SRCS})
|
||||
SET(Dialog_CPP_SRCS
|
||||
Clipping.cpp
|
||||
DemoMode.cpp
|
||||
DlgAbout.cpp
|
||||
DlgActivateWindowImp.cpp
|
||||
DlgCreateNewPreferencePackImp.cpp
|
||||
DlgUnitsCalculatorImp.cpp
|
||||
@@ -484,6 +485,7 @@ SET(Dialog_CPP_SRCS
|
||||
SET(Dialog_HPP_SRCS
|
||||
Clipping.h
|
||||
DemoMode.h
|
||||
DlgAbout.h
|
||||
DlgActivateWindowImp.h
|
||||
DlgCreateNewPreferencePackImp.h
|
||||
DlgUnitsCalculatorImp.h
|
||||
@@ -1088,7 +1090,7 @@ SET(Widget_CPP_SRCS
|
||||
ProgressDialog.cpp
|
||||
QuantitySpinBox.cpp
|
||||
SpinBox.cpp
|
||||
Splashscreen.cpp
|
||||
SplashScreen.cpp
|
||||
PythonWrapper.cpp
|
||||
UiLoader.cpp
|
||||
WidgetFactory.cpp
|
||||
@@ -1109,7 +1111,7 @@ SET(Widget_HPP_SRCS
|
||||
QuantitySpinBox.h
|
||||
QuantitySpinBox_p.h
|
||||
SpinBox.h
|
||||
Splashscreen.h
|
||||
SplashScreen.h
|
||||
PythonWrapper.h
|
||||
UiLoader.h
|
||||
WidgetFactory.h
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
#include "Action.h"
|
||||
#include "BitmapFactory.h"
|
||||
#include "Command.h"
|
||||
#include "DlgAbout.h"
|
||||
#include "DlgCustomizeImp.h"
|
||||
#include "DlgParameterImp.h"
|
||||
#include "DlgPreferencesImp.h"
|
||||
@@ -50,7 +51,6 @@
|
||||
#include "MainWindow.h"
|
||||
#include "OnlineDocumentation.h"
|
||||
#include "Selection.h"
|
||||
#include "Splashscreen.h"
|
||||
#include "WhatsThis.h"
|
||||
#include "Workbench.h"
|
||||
#include "WorkbenchManager.h"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,125 +1,104 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2004 Werner Mayer <wmayer[at]users.sourceforge.net> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library 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 Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef GUI_SPLASHSCREEN_H
|
||||
#define GUI_SPLASHSCREEN_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QSplashScreen>
|
||||
#include <QTextBrowser>
|
||||
#include <Gui/MDIView.h>
|
||||
|
||||
namespace Gui {
|
||||
|
||||
class SplashObserver;
|
||||
|
||||
/** This widget provides a splash screen that can be shown during application startup.
|
||||
*
|
||||
* \author Werner Mayer
|
||||
*/
|
||||
class SplashScreen : public QSplashScreen
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SplashScreen( const QPixmap & pixmap = QPixmap ( ), Qt::WindowFlags f = Qt::WindowFlags() );
|
||||
~SplashScreen() override;
|
||||
|
||||
void setShowMessages(bool on);
|
||||
|
||||
protected:
|
||||
void drawContents ( QPainter * painter ) override;
|
||||
|
||||
private:
|
||||
SplashObserver* messages;
|
||||
};
|
||||
|
||||
namespace Dialog {
|
||||
class Ui_AboutApplication;
|
||||
|
||||
class GuiExport AboutDialogFactory
|
||||
{
|
||||
public:
|
||||
AboutDialogFactory() = default;
|
||||
virtual ~AboutDialogFactory();
|
||||
|
||||
virtual QDialog *create(QWidget *parent) const;
|
||||
|
||||
static const AboutDialogFactory *defaultFactory();
|
||||
static void setDefaultFactory(AboutDialogFactory *factory);
|
||||
|
||||
private:
|
||||
static AboutDialogFactory* factory;
|
||||
};
|
||||
|
||||
class GuiExport LicenseView : public Gui::MDIView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit LicenseView(QWidget* parent=nullptr);
|
||||
~LicenseView() override;
|
||||
|
||||
void setSource(const QUrl & url);
|
||||
const char *getName() const override {
|
||||
return "LicenseView";
|
||||
}
|
||||
|
||||
private:
|
||||
QTextBrowser* browser;
|
||||
};
|
||||
|
||||
/** This widget provides the "About dialog" of an application.
|
||||
* This shows the current version, the build number and date.
|
||||
* \author Werner Mayer
|
||||
*/
|
||||
class GuiExport AboutDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit AboutDialog(QWidget* parent = nullptr);
|
||||
~AboutDialog() override;
|
||||
|
||||
protected:
|
||||
void setupLabels();
|
||||
void showCredits();
|
||||
void showLicenseInformation();
|
||||
QString getAdditionalLicenseInformation() const;
|
||||
void showLibraryInformation();
|
||||
void showCollectionInformation();
|
||||
void showPrivacyPolicy();
|
||||
void showOrHideImage(const QRect& rect);
|
||||
|
||||
protected:
|
||||
virtual void copyToClipboard();
|
||||
void linkActivated(const QUrl& link);
|
||||
|
||||
private:
|
||||
Ui_AboutApplication* ui;
|
||||
};
|
||||
|
||||
} // namespace Dialog
|
||||
} // namespace Gui
|
||||
|
||||
|
||||
#endif // GUI_SPLASHSCREEN_H
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2004 Werner Mayer <wmayer[at]users.sourceforge.net> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library 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 Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef GUI_DLG_ABOUT_H
|
||||
#define GUI_DLG_ABOUT_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QTextBrowser>
|
||||
#include <Gui/MDIView.h>
|
||||
|
||||
namespace Gui
|
||||
{
|
||||
namespace Dialog
|
||||
{
|
||||
|
||||
class Ui_AboutApplication;
|
||||
|
||||
class GuiExport AboutDialogFactory
|
||||
{
|
||||
public:
|
||||
AboutDialogFactory() = default;
|
||||
virtual ~AboutDialogFactory();
|
||||
|
||||
virtual QDialog* create(QWidget* parent) const;
|
||||
|
||||
static const AboutDialogFactory* defaultFactory();
|
||||
static void setDefaultFactory(AboutDialogFactory* factory);
|
||||
|
||||
private:
|
||||
static AboutDialogFactory* factory;
|
||||
};
|
||||
|
||||
class GuiExport LicenseView: public Gui::MDIView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit LicenseView(QWidget* parent = nullptr);
|
||||
~LicenseView() override;
|
||||
|
||||
void setSource(const QUrl& url);
|
||||
const char* getName() const override
|
||||
{
|
||||
return "LicenseView";
|
||||
}
|
||||
|
||||
private:
|
||||
QTextBrowser* browser;
|
||||
};
|
||||
|
||||
/** This widget provides the "About dialog" of an application.
|
||||
* This shows the current version, the build number and date.
|
||||
* \author Werner Mayer
|
||||
*/
|
||||
class GuiExport AboutDialog: public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit AboutDialog(QWidget* parent = nullptr);
|
||||
~AboutDialog() override;
|
||||
|
||||
protected:
|
||||
void setupLabels();
|
||||
void showCredits();
|
||||
void showLicenseInformation();
|
||||
QString getAdditionalLicenseInformation() const;
|
||||
void showLibraryInformation();
|
||||
void showCollectionInformation();
|
||||
void showPrivacyPolicy();
|
||||
void showOrHideImage(const QRect& rect);
|
||||
|
||||
protected:
|
||||
QPixmap aboutImage() const;
|
||||
virtual void copyToClipboard();
|
||||
void linkActivated(const QUrl& link);
|
||||
|
||||
private:
|
||||
Ui_AboutApplication* ui;
|
||||
};
|
||||
|
||||
} // namespace Dialog
|
||||
} // namespace Gui
|
||||
|
||||
#endif // GUI_DLG_ABOUT_H
|
||||
@@ -100,7 +100,7 @@
|
||||
#include "PythonConsole.h"
|
||||
#include "ReportView.h"
|
||||
#include "SelectionView.h"
|
||||
#include "Splashscreen.h"
|
||||
#include "SplashScreen.h"
|
||||
#include "ToolBarManager.h"
|
||||
#include "ToolBoxManager.h"
|
||||
#include "Tree.h"
|
||||
@@ -115,7 +115,6 @@
|
||||
#include "View3DInventor.h"
|
||||
#include "View3DInventorViewer.h"
|
||||
#include "DlgObjectSelection.h"
|
||||
#include "Tools.h"
|
||||
#include <App/Color.h>
|
||||
|
||||
FC_LOG_LEVEL_INIT("MainWindow",false,true,true)
|
||||
@@ -1634,9 +1633,7 @@ void MainWindow::delayedStartup()
|
||||
throw;
|
||||
}
|
||||
|
||||
const std::map<std::string,std::string>& cfg = App::Application::Config();
|
||||
auto it = cfg.find("StartHidden");
|
||||
if (it != cfg.end()) {
|
||||
if (Application::hiddenMainWindow()) {
|
||||
QApplication::quit();
|
||||
return;
|
||||
}
|
||||
@@ -1938,7 +1935,7 @@ void MainWindow::startSplasher()
|
||||
GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("General");
|
||||
// first search for an external image file
|
||||
if (hGrp->GetBool("ShowSplasher", true)) {
|
||||
d->splashscreen = new SplashScreen(this->splashImage());
|
||||
d->splashscreen = new SplashScreen(SplashScreen::splashImage());
|
||||
|
||||
if (!hGrp->GetBool("ShowSplasherMessages", false)) {
|
||||
d->splashscreen->setShowMessages(false);
|
||||
@@ -1961,220 +1958,6 @@ void MainWindow::stopSplasher()
|
||||
}
|
||||
}
|
||||
|
||||
QPixmap MainWindow::aboutImage() const
|
||||
{
|
||||
// See if we have a custom About screen image set
|
||||
QPixmap about_image;
|
||||
QFileInfo fi(QString::fromLatin1("images:about_image.png"));
|
||||
if (fi.isFile() && fi.exists())
|
||||
about_image.load(fi.filePath(), "PNG");
|
||||
|
||||
std::string about_path = App::Application::Config()["AboutImage"];
|
||||
if (!about_path.empty() && about_image.isNull()) {
|
||||
QString path = QString::fromUtf8(about_path.c_str());
|
||||
if (QDir(path).isRelative()) {
|
||||
QString home = QString::fromStdString(App::Application::getHomePath());
|
||||
path = QFileInfo(QDir(home), path).absoluteFilePath();
|
||||
}
|
||||
about_image.load(path);
|
||||
|
||||
// Now try the icon paths
|
||||
if (about_image.isNull()) {
|
||||
about_image = Gui::BitmapFactory().pixmap(about_path.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return about_image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a warning about this being a developer build. Designed for display in the Splashscreen.
|
||||
* \param painter The painter to draw the warning into
|
||||
* \param startPosition The painter-space coordinates to start the warning box at.
|
||||
* \param maxSize The maximum extents for the box that is drawn. If the text exceeds this size it
|
||||
* will be scaled down to fit.
|
||||
* \note The text string is translatable, so its length is somewhat unpredictable. It is always
|
||||
* displayed as two lines, regardless of the length of the text (e.g. no wrapping is done). Only the
|
||||
* width is considered, the height simply follows from the font size.
|
||||
*/
|
||||
void MainWindow::renderDevBuildWarning(
|
||||
QPainter &painter,
|
||||
const QPoint startPosition,
|
||||
const QSize maxSize,
|
||||
QColor color)
|
||||
{
|
||||
// Create a background box that fades out the artwork for better legibility
|
||||
QColor fader (Qt::white);
|
||||
constexpr float halfDensity (0.65F);
|
||||
fader.setAlphaF(halfDensity);
|
||||
QBrush fillBrush(fader, Qt::BrushStyle::SolidPattern);
|
||||
painter.setBrush(fillBrush);
|
||||
|
||||
// Construct the lines of text and figure out how much space they need
|
||||
const auto devWarningLine1 = tr("WARNING: This is a development version.");
|
||||
const auto devWarningLine2 = tr("Please do not use it in a production environment.");
|
||||
QFontMetrics fontMetrics(painter.font()); // Try to use the existing font
|
||||
int padding = QtTools::horizontalAdvance(fontMetrics, QLatin1String("M")); // Arbitrary
|
||||
int line1Width = QtTools::horizontalAdvance(fontMetrics, devWarningLine1);
|
||||
int line2Width = QtTools::horizontalAdvance(fontMetrics, devWarningLine2);
|
||||
int boxWidth = std::max(line1Width,line2Width) + 2 * padding;
|
||||
int lineHeight = fontMetrics.lineSpacing();
|
||||
if (boxWidth > maxSize.width()) {
|
||||
// Especially if the text was translated, there is a chance that using the existing font
|
||||
// will exceed the width of the Splashscreen graphic. Resize down so that it fits, no matter
|
||||
// how long the text strings are.
|
||||
float reductionFactor = static_cast<float>(maxSize.width()) / static_cast<float>(boxWidth);
|
||||
int newFontSize = static_cast<int>(painter.font().pointSize() * reductionFactor);
|
||||
padding *= reductionFactor;
|
||||
QFont newFont = painter.font();
|
||||
newFont.setPointSize(newFontSize);
|
||||
painter.setFont(newFont);
|
||||
lineHeight = painter.fontMetrics().lineSpacing();
|
||||
boxWidth = maxSize.width();
|
||||
}
|
||||
constexpr float lineExpansionFactor(2.3F);
|
||||
int boxHeight = static_cast<int>(lineHeight*lineExpansionFactor);
|
||||
|
||||
// Draw the background rectangle and the text
|
||||
painter.setPen(color);
|
||||
painter.drawRect(startPosition.x(), startPosition.y(), boxWidth, boxHeight);
|
||||
painter.drawText(startPosition.x()+padding, startPosition.y()+lineHeight, devWarningLine1);
|
||||
painter.drawText(startPosition.x()+padding, startPosition.y()+2*lineHeight, devWarningLine2);
|
||||
}
|
||||
|
||||
QPixmap MainWindow::splashImage() const
|
||||
{
|
||||
// search in the UserAppData dir as very first
|
||||
QPixmap splash_image;
|
||||
QFileInfo fi(QString::fromLatin1("images:splash_image.png"));
|
||||
if (fi.isFile() && fi.exists())
|
||||
splash_image.load(fi.filePath(), "PNG");
|
||||
|
||||
// if no image was found try the config
|
||||
std::string splash_path = App::Application::Config()["SplashScreen"];
|
||||
if (splash_image.isNull()) {
|
||||
QString path = QString::fromUtf8(splash_path.c_str());
|
||||
if (QDir(path).isRelative()) {
|
||||
QString home = QString::fromStdString(App::Application::getHomePath());
|
||||
path = QFileInfo(QDir(home), path).absoluteFilePath();
|
||||
}
|
||||
|
||||
splash_image.load(path);
|
||||
}
|
||||
|
||||
// now try the icon paths
|
||||
float pixelRatio (1.0);
|
||||
if (splash_image.isNull()) {
|
||||
// determine the count of splashes
|
||||
QStringList pixmaps = Gui::BitmapFactory().findIconFiles().filter(QString::fromStdString(splash_path));
|
||||
// divide by 2 since there's two sets (normal and 2x)
|
||||
// minus 1 to ignore the default splash that isn't numbered
|
||||
int splash_count = pixmaps.count()/2 - 1;
|
||||
|
||||
// set a random splash path
|
||||
if (splash_count > 0) {
|
||||
int random = rand() % splash_count;
|
||||
splash_path += std::to_string(random);
|
||||
}
|
||||
if (qApp->devicePixelRatio() > 1.0) {
|
||||
// For HiDPI screens, we have a double-resolution version of the splash image
|
||||
splash_path += "_2x";
|
||||
splash_image = Gui::BitmapFactory().pixmap(splash_path.c_str());
|
||||
splash_image.setDevicePixelRatio(2.0);
|
||||
pixelRatio = 2.0;
|
||||
}
|
||||
else {
|
||||
splash_image = Gui::BitmapFactory().pixmap(splash_path.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// include application name and version number
|
||||
std::map<std::string,std::string>::const_iterator tc = App::Application::Config().find("SplashInfoColor");
|
||||
std::map<std::string,std::string>::const_iterator wc = App::Application::Config().find("SplashWarningColor");
|
||||
if (tc != App::Application::Config().end() && wc != App::Application::Config().end()) {
|
||||
QString title = qApp->applicationName();
|
||||
QString major = QString::fromLatin1(App::Application::Config()["BuildVersionMajor"].c_str());
|
||||
QString minor = QString::fromLatin1(App::Application::Config()["BuildVersionMinor"].c_str());
|
||||
QString point = QString::fromLatin1(App::Application::Config()["BuildVersionPoint"].c_str());
|
||||
QString suffix = QString::fromLatin1(App::Application::Config()["BuildVersionSuffix"].c_str());
|
||||
QString version = QString::fromLatin1("%1.%2.%3%4").arg(major, minor, point, suffix);
|
||||
QString position, fontFamily;
|
||||
|
||||
std::map<std::string,std::string>::const_iterator te = App::Application::Config().find("SplashInfoExeName");
|
||||
std::map<std::string,std::string>::const_iterator tv = App::Application::Config().find("SplashInfoVersion");
|
||||
std::map<std::string,std::string>::const_iterator tp = App::Application::Config().find("SplashInfoPosition");
|
||||
std::map<std::string,std::string>::const_iterator tf = App::Application::Config().find("SplashInfoFont");
|
||||
if (te != App::Application::Config().end())
|
||||
title = QString::fromUtf8(te->second.c_str());
|
||||
if (tv != App::Application::Config().end())
|
||||
version = QString::fromUtf8(tv->second.c_str());
|
||||
if (tp != App::Application::Config().end())
|
||||
position = QString::fromUtf8(tp->second.c_str());
|
||||
if (tf != App::Application::Config().end())
|
||||
fontFamily = QString::fromUtf8(tf->second.c_str());
|
||||
|
||||
QPainter painter;
|
||||
painter.begin(&splash_image);
|
||||
if (!fontFamily.isEmpty()) {
|
||||
QFont font = painter.font();
|
||||
if (font.fromString(fontFamily))
|
||||
painter.setFont(font);
|
||||
}
|
||||
|
||||
QFont fontExe = painter.font();
|
||||
fontExe.setPointSizeF(20.0);
|
||||
QFontMetrics metricExe(fontExe);
|
||||
int l = QtTools::horizontalAdvance(metricExe, title);
|
||||
if (title == QLatin1String("FreeCAD")) {
|
||||
l = 0.0; // "FreeCAD" text is already part of the splashscreen, version goes below it
|
||||
}
|
||||
int w = splash_image.width();
|
||||
int h = splash_image.height();
|
||||
|
||||
QFont fontVer = painter.font();
|
||||
fontVer.setPointSizeF(11.0);
|
||||
QFontMetrics metricVer(fontVer);
|
||||
int v = QtTools::horizontalAdvance(metricVer, version);
|
||||
|
||||
int x = -1, y = -1;
|
||||
QRegularExpression rx(QLatin1String("(\\d+).(\\d+)"));
|
||||
auto match = rx.match(position);
|
||||
if (match.hasMatch()) {
|
||||
x = match.captured(1).toInt();
|
||||
y = match.captured(2).toInt();
|
||||
}
|
||||
else {
|
||||
x = w - (l + v + 10);
|
||||
y = h - 20;
|
||||
}
|
||||
|
||||
QColor color(QString::fromLatin1(tc->second.c_str()));
|
||||
if (color.isValid()) {
|
||||
painter.setPen(color);
|
||||
painter.setFont(fontExe);
|
||||
if (title != QLatin1String("FreeCAD")) {
|
||||
// FreeCAD's Splashscreen already contains the EXE name, no need to draw it
|
||||
painter.drawText(x, y, title);
|
||||
}
|
||||
painter.setFont(fontVer);
|
||||
painter.drawText(x + (l + 235), y - 7, version);
|
||||
QColor warningColor(QString::fromLatin1(wc->second.c_str()));
|
||||
if (suffix == QLatin1String("dev") && warningColor.isValid()) {
|
||||
fontVer.setPointSizeF(14.0);
|
||||
painter.setFont(fontVer);
|
||||
const int lineHeight = metricVer.lineSpacing();
|
||||
const int padding {45}; // Distance from the edge of the graphic's bounding box
|
||||
QPoint startPosition(padding, y + lineHeight*2);
|
||||
QSize maxSize(w/pixelRatio - 2*padding, lineHeight * 3);
|
||||
MainWindow::renderDevBuildWarning(painter, startPosition, maxSize, warningColor);
|
||||
}
|
||||
painter.end();
|
||||
}
|
||||
}
|
||||
|
||||
return splash_image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops the event \a e and tries to open the files.
|
||||
*/
|
||||
|
||||
@@ -142,10 +142,6 @@ public:
|
||||
void startSplasher();
|
||||
/** Stops the splasher after startup. */
|
||||
void stopSplasher();
|
||||
/* The image of the About dialog, it might be empty. */
|
||||
QPixmap aboutImage() const;
|
||||
/* The image of the splash screen of the application. */
|
||||
QPixmap splashImage() const;
|
||||
/** Shows the online documentation. */
|
||||
void showDocumentation(const QString& help);
|
||||
//@}
|
||||
|
||||
404
src/Gui/SplashScreen.cpp
Normal file
404
src/Gui/SplashScreen.cpp
Normal file
@@ -0,0 +1,404 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2004 Werner Mayer <wmayer[at]users.sourceforge.net> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library 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 Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "PreCompiled.h"
|
||||
|
||||
#ifndef _PreComp_
|
||||
#include <cstdlib>
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QMutex>
|
||||
#include <QPainter>
|
||||
#include <QRegularExpression>
|
||||
#include <QRegularExpressionMatch>
|
||||
#include <QWaitCondition>
|
||||
#endif
|
||||
|
||||
#include <App/Application.h>
|
||||
#include <App/Metadata.h>
|
||||
#include <Base/Console.h>
|
||||
|
||||
#include "BitmapFactory.h"
|
||||
#include "SplashScreen.h"
|
||||
#include "Tools.h"
|
||||
|
||||
using namespace Gui;
|
||||
|
||||
namespace Gui
|
||||
{
|
||||
|
||||
/** Displays all messages at startup inside the splash screen.
|
||||
* \author Werner Mayer
|
||||
*/
|
||||
class SplashObserver: public Base::ILogger
|
||||
{
|
||||
public:
|
||||
SplashObserver(const SplashObserver&) = delete;
|
||||
SplashObserver(SplashObserver&&) = delete;
|
||||
SplashObserver& operator=(const SplashObserver&) = delete;
|
||||
SplashObserver& operator=(SplashObserver&&) = delete;
|
||||
|
||||
explicit SplashObserver(QSplashScreen* splasher = nullptr)
|
||||
: splash(splasher)
|
||||
, alignment(Qt::AlignBottom | Qt::AlignLeft)
|
||||
, textColor(Qt::black)
|
||||
{
|
||||
Base::Console().AttachObserver(this);
|
||||
|
||||
// allow to customize text position and color
|
||||
const std::map<std::string, std::string>& cfg = App::Application::Config();
|
||||
auto al = cfg.find("SplashAlignment");
|
||||
if (al != cfg.end()) {
|
||||
QString alt = QString::fromLatin1(al->second.c_str());
|
||||
int align = 0;
|
||||
if (alt.startsWith(QLatin1String("VCenter"))) {
|
||||
align = Qt::AlignVCenter;
|
||||
}
|
||||
else if (alt.startsWith(QLatin1String("Top"))) {
|
||||
align = Qt::AlignTop;
|
||||
}
|
||||
else {
|
||||
align = Qt::AlignBottom;
|
||||
}
|
||||
|
||||
if (alt.endsWith(QLatin1String("HCenter"))) {
|
||||
align += Qt::AlignHCenter;
|
||||
}
|
||||
else if (alt.endsWith(QLatin1String("Right"))) {
|
||||
align += Qt::AlignRight;
|
||||
}
|
||||
else {
|
||||
align += Qt::AlignLeft;
|
||||
}
|
||||
|
||||
alignment = align;
|
||||
}
|
||||
|
||||
// choose text color
|
||||
auto tc = cfg.find("SplashTextColor");
|
||||
if (tc != cfg.end()) {
|
||||
QColor col(QString::fromStdString(tc->second));
|
||||
if (col.isValid()) {
|
||||
textColor = col;
|
||||
}
|
||||
}
|
||||
}
|
||||
~SplashObserver() override
|
||||
{
|
||||
Base::Console().DetachObserver(this);
|
||||
}
|
||||
const char* Name() override
|
||||
{
|
||||
return "SplashObserver";
|
||||
}
|
||||
void SendLog(const std::string& notifiername,
|
||||
const std::string& msg,
|
||||
Base::LogStyle level,
|
||||
Base::IntendedRecipient recipient,
|
||||
Base::ContentType content) override
|
||||
{
|
||||
Q_UNUSED(notifiername)
|
||||
Q_UNUSED(recipient)
|
||||
Q_UNUSED(content)
|
||||
|
||||
#ifdef FC_DEBUG
|
||||
Q_UNUSED(level)
|
||||
Log(msg);
|
||||
#else
|
||||
if (level == Base::LogStyle::Log) {
|
||||
Log(msg);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
void Log(const std::string& text)
|
||||
{
|
||||
QString msg(QString::fromStdString(text));
|
||||
QRegularExpression rx;
|
||||
// ignore 'Init:' and 'Mod:' prefixes
|
||||
rx.setPattern(QLatin1String("^\\s*(Init:|Mod:)\\s*"));
|
||||
auto match = rx.match(msg);
|
||||
if (match.hasMatch()) {
|
||||
msg = msg.mid(match.capturedLength());
|
||||
}
|
||||
else {
|
||||
// ignore activation of commands
|
||||
rx.setPattern(QLatin1String(R"(^\s*(\+App::|Create|CmdC:|CmdG:|Act:)\s*)"));
|
||||
match = rx.match(msg);
|
||||
if (match.hasMatch() && match.capturedStart() == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
splash->showMessage(msg.replace(QLatin1String("\n"), QString()), alignment, textColor);
|
||||
QMutex mutex;
|
||||
QMutexLocker ml(&mutex);
|
||||
QWaitCondition().wait(&mutex, 50);
|
||||
}
|
||||
|
||||
private:
|
||||
QSplashScreen* splash;
|
||||
int alignment;
|
||||
QColor textColor;
|
||||
};
|
||||
|
||||
/**
|
||||
* Displays a warning about this being a developer build. Designed for display in the Splashscreen.
|
||||
* \param painter The painter to draw the warning into
|
||||
* \param startPosition The painter-space coordinates to start the warning box at.
|
||||
* \param maxSize The maximum extents for the box that is drawn. If the text exceeds this size it
|
||||
* will be scaled down to fit.
|
||||
* \note The text string is translatable, so its length is somewhat unpredictable. It is always
|
||||
* displayed as two lines, regardless of the length of the text (e.g. no wrapping is done). Only the
|
||||
* width is considered, the height simply follows from the font size.
|
||||
*/
|
||||
static void renderDevBuildWarning(QPainter& painter,
|
||||
const QPoint startPosition,
|
||||
const QSize maxSize,
|
||||
QColor color)
|
||||
{
|
||||
// Create a background box that fades out the artwork for better legibility
|
||||
QColor fader(Qt::white);
|
||||
constexpr float halfDensity(0.65F);
|
||||
fader.setAlphaF(halfDensity);
|
||||
QBrush fillBrush(fader, Qt::BrushStyle::SolidPattern);
|
||||
painter.setBrush(fillBrush);
|
||||
|
||||
// Construct the lines of text and figure out how much space they need
|
||||
const auto devWarningLine1 = QObject::tr("WARNING: This is a development version.");
|
||||
const auto devWarningLine2 = QObject::tr("Please do not use it in a production environment.");
|
||||
QFontMetrics fontMetrics(painter.font()); // Try to use the existing font
|
||||
int padding = QtTools::horizontalAdvance(fontMetrics, QLatin1String("M")); // Arbitrary
|
||||
int line1Width = QtTools::horizontalAdvance(fontMetrics, devWarningLine1);
|
||||
int line2Width = QtTools::horizontalAdvance(fontMetrics, devWarningLine2);
|
||||
int boxWidth = std::max(line1Width, line2Width) + 2 * padding;
|
||||
int lineHeight = fontMetrics.lineSpacing();
|
||||
if (boxWidth > maxSize.width()) {
|
||||
// Especially if the text was translated, there is a chance that using the existing font
|
||||
// will exceed the width of the Splashscreen graphic. Resize down so that it fits, no matter
|
||||
// how long the text strings are.
|
||||
float reductionFactor = static_cast<float>(maxSize.width()) / static_cast<float>(boxWidth);
|
||||
int newFontSize = static_cast<int>(painter.font().pointSize() * reductionFactor);
|
||||
padding *= reductionFactor;
|
||||
QFont newFont = painter.font();
|
||||
newFont.setPointSize(newFontSize);
|
||||
painter.setFont(newFont);
|
||||
lineHeight = painter.fontMetrics().lineSpacing();
|
||||
boxWidth = maxSize.width();
|
||||
}
|
||||
constexpr float lineExpansionFactor(2.3F);
|
||||
int boxHeight = static_cast<int>(lineHeight * lineExpansionFactor);
|
||||
|
||||
// Draw the background rectangle and the text
|
||||
painter.setPen(color);
|
||||
painter.drawRect(startPosition.x(), startPosition.y(), boxWidth, boxHeight);
|
||||
painter.drawText(startPosition.x() + padding, startPosition.y() + lineHeight, devWarningLine1);
|
||||
painter.drawText(startPosition.x() + padding,
|
||||
startPosition.y() + 2 * lineHeight,
|
||||
devWarningLine2);
|
||||
}
|
||||
|
||||
} // namespace Gui
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs a splash screen that will display the pixmap.
|
||||
*/
|
||||
SplashScreen::SplashScreen(const QPixmap& pixmap, Qt::WindowFlags f)
|
||||
: QSplashScreen(pixmap, f)
|
||||
{
|
||||
// write the messages to splasher
|
||||
messages = new SplashObserver(this);
|
||||
}
|
||||
|
||||
/** Destruction. */
|
||||
SplashScreen::~SplashScreen()
|
||||
{
|
||||
delete messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the contents of the splash screen using painter \a painter. The default
|
||||
* implementation draws the message passed by message().
|
||||
*/
|
||||
void SplashScreen::drawContents(QPainter* painter)
|
||||
{
|
||||
QSplashScreen::drawContents(painter);
|
||||
}
|
||||
|
||||
void SplashScreen::setShowMessages(bool on)
|
||||
{
|
||||
messages->bErr = on;
|
||||
messages->bMsg = on;
|
||||
messages->bLog = on;
|
||||
messages->bWrn = on;
|
||||
}
|
||||
|
||||
QPixmap SplashScreen::splashImage()
|
||||
{
|
||||
// search in the UserAppData dir as very first
|
||||
QPixmap splash_image;
|
||||
QFileInfo fi(QString::fromLatin1("images:splash_image.png"));
|
||||
if (fi.isFile() && fi.exists()) {
|
||||
splash_image.load(fi.filePath(), "PNG");
|
||||
}
|
||||
|
||||
// if no image was found try the config
|
||||
std::string splash_path = App::Application::Config()["SplashScreen"];
|
||||
if (splash_image.isNull()) {
|
||||
QString path = QString::fromStdString(splash_path);
|
||||
if (QDir(path).isRelative()) {
|
||||
QString home = QString::fromStdString(App::Application::getHomePath());
|
||||
path = QFileInfo(QDir(home), path).absoluteFilePath();
|
||||
}
|
||||
|
||||
splash_image.load(path);
|
||||
}
|
||||
|
||||
// now try the icon paths
|
||||
float pixelRatio(1.0);
|
||||
if (splash_image.isNull()) {
|
||||
// determine the count of splashes
|
||||
QStringList pixmaps =
|
||||
Gui::BitmapFactory().findIconFiles().filter(QString::fromStdString(splash_path));
|
||||
// divide by 2 since there's two sets (normal and 2x)
|
||||
// minus 1 to ignore the default splash that isn't numbered
|
||||
int splash_count = pixmaps.count() / 2 - 1;
|
||||
|
||||
// set a random splash path
|
||||
if (splash_count > 0) {
|
||||
int random = rand() % splash_count;
|
||||
splash_path += std::to_string(random);
|
||||
}
|
||||
|
||||
if (qApp->devicePixelRatio() > 1.0) {
|
||||
// For HiDPI screens, we have a double-resolution version of the splash image
|
||||
splash_path += "_2x";
|
||||
splash_image = Gui::BitmapFactory().pixmap(splash_path.c_str());
|
||||
splash_image.setDevicePixelRatio(2.0);
|
||||
pixelRatio = 2.0;
|
||||
}
|
||||
else {
|
||||
splash_image = Gui::BitmapFactory().pixmap(splash_path.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// include application name and version number
|
||||
std::map<std::string, std::string>::const_iterator tc =
|
||||
App::Application::Config().find("SplashInfoColor");
|
||||
std::map<std::string, std::string>::const_iterator wc =
|
||||
App::Application::Config().find("SplashWarningColor");
|
||||
if (tc != App::Application::Config().end() && wc != App::Application::Config().end()) {
|
||||
QString title = qApp->applicationName();
|
||||
QString major = QString::fromStdString(App::Application::Config()["BuildVersionMajor"]);
|
||||
QString minor = QString::fromStdString(App::Application::Config()["BuildVersionMinor"]);
|
||||
QString point = QString::fromStdString(App::Application::Config()["BuildVersionPoint"]);
|
||||
QString suffix = QString::fromStdString(App::Application::Config()["BuildVersionSuffix"]);
|
||||
QString version = QString::fromLatin1("%1.%2.%3%4").arg(major, minor, point, suffix);
|
||||
QString position, fontFamily;
|
||||
|
||||
std::map<std::string, std::string>::const_iterator te =
|
||||
App::Application::Config().find("SplashInfoExeName");
|
||||
std::map<std::string, std::string>::const_iterator tv =
|
||||
App::Application::Config().find("SplashInfoVersion");
|
||||
std::map<std::string, std::string>::const_iterator tp =
|
||||
App::Application::Config().find("SplashInfoPosition");
|
||||
std::map<std::string, std::string>::const_iterator tf =
|
||||
App::Application::Config().find("SplashInfoFont");
|
||||
if (te != App::Application::Config().end()) {
|
||||
title = QString::fromStdString(te->second);
|
||||
}
|
||||
if (tv != App::Application::Config().end()) {
|
||||
version = QString::fromStdString(tv->second);
|
||||
}
|
||||
if (tp != App::Application::Config().end()) {
|
||||
position = QString::fromStdString(tp->second);
|
||||
}
|
||||
if (tf != App::Application::Config().end()) {
|
||||
fontFamily = QString::fromStdString(tf->second);
|
||||
}
|
||||
|
||||
QPainter painter;
|
||||
painter.begin(&splash_image);
|
||||
if (!fontFamily.isEmpty()) {
|
||||
QFont font = painter.font();
|
||||
if (font.fromString(fontFamily)) {
|
||||
painter.setFont(font);
|
||||
}
|
||||
}
|
||||
|
||||
QFont fontExe = painter.font();
|
||||
fontExe.setPointSizeF(20.0);
|
||||
QFontMetrics metricExe(fontExe);
|
||||
int l = QtTools::horizontalAdvance(metricExe, title);
|
||||
if (title == QLatin1String("FreeCAD")) {
|
||||
l = 0.0; // "FreeCAD" text is already part of the splashscreen, version goes below it
|
||||
}
|
||||
int w = splash_image.width();
|
||||
int h = splash_image.height();
|
||||
|
||||
QFont fontVer = painter.font();
|
||||
fontVer.setPointSizeF(11.0);
|
||||
QFontMetrics metricVer(fontVer);
|
||||
int v = QtTools::horizontalAdvance(metricVer, version);
|
||||
|
||||
int x = -1, y = -1;
|
||||
QRegularExpression rx(QLatin1String("(\\d+).(\\d+)"));
|
||||
auto match = rx.match(position);
|
||||
if (match.hasMatch()) {
|
||||
x = match.captured(1).toInt();
|
||||
y = match.captured(2).toInt();
|
||||
}
|
||||
else {
|
||||
x = w - (l + v + 10);
|
||||
y = h - 20;
|
||||
}
|
||||
|
||||
QColor color(QString::fromStdString(tc->second));
|
||||
if (color.isValid()) {
|
||||
painter.setPen(color);
|
||||
painter.setFont(fontExe);
|
||||
if (title != QLatin1String("FreeCAD")) {
|
||||
// FreeCAD's Splashscreen already contains the EXE name, no need to draw it
|
||||
painter.drawText(x, y, title);
|
||||
}
|
||||
painter.setFont(fontVer);
|
||||
painter.drawText(x + (l + 235), y - 7, version);
|
||||
QColor warningColor(QString::fromStdString(wc->second));
|
||||
if (suffix == QLatin1String("dev") && warningColor.isValid()) {
|
||||
fontVer.setPointSizeF(14.0);
|
||||
painter.setFont(fontVer);
|
||||
const int lineHeight = metricVer.lineSpacing();
|
||||
const int padding {45}; // Distance from the edge of the graphic's bounding box
|
||||
QPoint startPosition(padding, y + lineHeight * 2);
|
||||
QSize maxSize(w / pixelRatio - 2 * padding, lineHeight * 3);
|
||||
renderDevBuildWarning(painter, startPosition, maxSize, warningColor);
|
||||
}
|
||||
painter.end();
|
||||
}
|
||||
}
|
||||
|
||||
return splash_image;
|
||||
}
|
||||
58
src/Gui/SplashScreen.h
Normal file
58
src/Gui/SplashScreen.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/***************************************************************************
|
||||
* Copyright (c) 2004 Werner Mayer <wmayer[at]users.sourceforge.net> *
|
||||
* *
|
||||
* This file is part of the FreeCAD CAx development system. *
|
||||
* *
|
||||
* This library is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU Library General Public *
|
||||
* License as published by the Free Software Foundation; either *
|
||||
* version 2 of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This library 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 Library General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Library General Public *
|
||||
* License along with this library; see the file COPYING.LIB. If not, *
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place, *
|
||||
* Suite 330, Boston, MA 02111-1307, USA *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef GUI_SPLASHSCREEN_H
|
||||
#define GUI_SPLASHSCREEN_H
|
||||
|
||||
#include <QSplashScreen>
|
||||
|
||||
namespace Gui
|
||||
{
|
||||
|
||||
class SplashObserver;
|
||||
|
||||
/** This widget provides a splash screen that can be shown during application startup.
|
||||
*
|
||||
* \author Werner Mayer
|
||||
*/
|
||||
class SplashScreen: public QSplashScreen
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SplashScreen(const QPixmap& pixmap = QPixmap(), Qt::WindowFlags f = Qt::WindowFlags());
|
||||
~SplashScreen() override;
|
||||
|
||||
void setShowMessages(bool on);
|
||||
|
||||
static QPixmap splashImage();
|
||||
|
||||
protected:
|
||||
void drawContents(QPainter* painter) override;
|
||||
|
||||
private:
|
||||
SplashObserver* messages;
|
||||
};
|
||||
|
||||
} // namespace Gui
|
||||
|
||||
#endif // GUI_SPLASHSCREEN_H
|
||||
@@ -107,6 +107,8 @@ void StartupProcess::setupApplication()
|
||||
// compression for tablet events here to solve that.
|
||||
QCoreApplication::setAttribute(Qt::AA_CompressTabletEvents);
|
||||
#endif
|
||||
// https://forum.freecad.org/viewtopic.php?f=3&t=15540
|
||||
QApplication::setAttribute(Qt::AA_DontShowIconsInMenus, false);
|
||||
}
|
||||
|
||||
void StartupProcess::execute()
|
||||
@@ -408,24 +410,10 @@ void StartupPostProcess::setImportImageFormats()
|
||||
App::GetApplication().addImportType(filter.c_str(), "FreeCADGui");
|
||||
}
|
||||
|
||||
bool StartupPostProcess::hiddenMainWindow() const
|
||||
{
|
||||
const std::map<std::string,std::string>& cfg = App::Application::Config();
|
||||
bool hidden = false;
|
||||
auto it = cfg.find("StartHidden");
|
||||
if (it != cfg.end()) {
|
||||
hidden = true;
|
||||
}
|
||||
|
||||
return hidden;
|
||||
}
|
||||
|
||||
void StartupPostProcess::showMainWindow()
|
||||
{
|
||||
bool hidden = hiddenMainWindow();
|
||||
|
||||
// show splasher while initializing the GUI
|
||||
if (!hidden && !loadFromPythonModule) {
|
||||
if (!Application::hiddenMainWindow() && !loadFromPythonModule) {
|
||||
mainWindow->startSplasher();
|
||||
}
|
||||
|
||||
@@ -488,7 +476,7 @@ void StartupPostProcess::activateWorkbench()
|
||||
guiApp.activateWorkbench(start.c_str());
|
||||
|
||||
// show the main window
|
||||
if (!hiddenMainWindow()) {
|
||||
if (!Application::hiddenMainWindow()) {
|
||||
Base::Console().Log("Init: Showing main window\n");
|
||||
mainWindow->loadWindowSettings();
|
||||
}
|
||||
|
||||
@@ -72,7 +72,6 @@ private:
|
||||
void setStyleSheet();
|
||||
void autoloadModules(const QStringList& wb);
|
||||
void setImportImageFormats();
|
||||
bool hiddenMainWindow() const;
|
||||
void showMainWindow();
|
||||
void activateWorkbench();
|
||||
void checkParameters();
|
||||
|
||||
Reference in New Issue
Block a user