diff --git a/.gitmodules b/.gitmodules
index 42c9b757f4..b6bc63efd4 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -4,3 +4,6 @@
[submodule "tests/lib"]
path = tests/lib
url = https://github.com/google/googletest
+[submodule "src/3rdParty/GSL"]
+ path = src/3rdParty/GSL
+ url = https://github.com/microsoft/GSL
diff --git a/cMake/FreeCAD_Helpers/PrintFinalReport.cmake b/cMake/FreeCAD_Helpers/PrintFinalReport.cmake
index 70cb84edef..adb4c97a13 100644
--- a/cMake/FreeCAD_Helpers/PrintFinalReport.cmake
+++ b/cMake/FreeCAD_Helpers/PrintFinalReport.cmake
@@ -88,11 +88,39 @@ macro(PrintFinalReport)
value(FREECAD_CREATE_MAC_APP)
value(FREECAD_USE_PYBIND11)
value(FREECAD_USE_EXTERNAL_KDL)
+ value(BUILD_ADDONMGR)
+ value(BUILD_ARCH)
+ value(BUILD_ASSEMBLY)
+ value(BUILD_CLOUD)
+ value(BUILD_DRAFT)
+ value(BUILD_DRAWING)
value(BUILD_FEM)
- value(BUILD_WEB)
- value(BUILD_GUI)
+ value(BUILD_HELP)
+ value(BUILD_IDF)
+ value(BUILD_IMPORT)
+ value(BUILD_INSPECTION)
+ value(BUILD_JTREADER)
+ value(BUILD_MATERIAL)
+ value(BUILD_MESH)
+ value(BUILD_MESH_PART)
+ value(BUILD_OPENSCAD)
+ value(BUILD_PART)
+ value(BUILD_PART_DESIGN)
+ value(BUILD_PATH)
+ value(BUILD_PLOT)
+ value(BUILD_POINTS)
+ value(BUILD_REVERSEENGINEERING)
+ value(BUILD_ROBOT)
+ value(BUILD_SANDBOX)
+ value(BUILD_SHOW)
+ value(BUILD_SKETCHER)
+ value(BUILD_SPREADSHEET)
value(BUILD_START)
+ value(BUILD_SURFACE)
value(BUILD_TECHDRAW)
+ value(BUILD_TEST)
+ value(BUILD_TUX)
+ value(BUILD_WEB)
value(CMAKE_INSTALL_PREFIX)
value(USE_CUDA)
value(USE_OPENCV)
diff --git a/src/3rdParty/GSL b/src/3rdParty/GSL
new file mode 160000
index 0000000000..b39e7e4b09
--- /dev/null
+++ b/src/3rdParty/GSL
@@ -0,0 +1 @@
+Subproject commit b39e7e4b0987859f5b19ff7686b149c916588658
diff --git a/src/Gui/Icons/resource.qrc b/src/Gui/Icons/resource.qrc
index 95fc934f8e..a5bf4c9b2c 100644
--- a/src/Gui/Icons/resource.qrc
+++ b/src/Gui/Icons/resource.qrc
@@ -12,6 +12,7 @@
list-add.svg
freecad.svg
freecad-doc.png
+ freecad-doc.svg
bulb.svg
TextDocument.svg
button_down.svg
diff --git a/src/Mod/Assembly/Gui/Resources/Assembly.qrc b/src/Mod/Assembly/Gui/Resources/Assembly.qrc
index ad258eecf3..82325ce87a 100644
--- a/src/Mod/Assembly/Gui/Resources/Assembly.qrc
+++ b/src/Mod/Assembly/Gui/Resources/Assembly.qrc
@@ -16,5 +16,6 @@
panels/TaskAssemblyInsertLink.ui
preferences/Assembly.ui
icons/Assembly_CreateJointDistance.svg
+ icons/AssemblyWorkbench.svg
diff --git a/src/Mod/CMakeLists.txt b/src/Mod/CMakeLists.txt
index 896402098f..7873061e5c 100644
--- a/src/Mod/CMakeLists.txt
+++ b/src/Mod/CMakeLists.txt
@@ -10,6 +10,10 @@ if(BUILD_ASSEMBLY)
add_subdirectory(Assembly)
endif(BUILD_ASSEMBLY)
+if(BUILD_CLEANSTART)
+ add_subdirectory(CleanStart)
+endif(BUILD_CLEANSTART)
+
if(BUILD_CLOUD)
add_subdirectory(Cloud)
endif(BUILD_CLOUD)
diff --git a/src/Mod/CleanStart/App/AppCleanStart.cpp b/src/Mod/CleanStart/App/AppCleanStart.cpp
new file mode 100644
index 0000000000..f0d300cc67
--- /dev/null
+++ b/src/Mod/CleanStart/App/AppCleanStart.cpp
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/****************************************************************************
+ * *
+# Copyright (c) 2024 The FreeCAD Project Association AISBL *
+ * *
+ * This file is part of FreeCAD. *
+ * *
+ * FreeCAD is free software: you can redistribute it and/or modify it *
+ * under the terms of the GNU Lesser General Public License as *
+ * published by the Free Software Foundation, either version 2.1 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * FreeCAD 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 *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with FreeCAD. If not, see *
+ * . *
+ * *
+ ***************************************************************************/
+
+#include "PreCompiled.h"
+
+#include
+#include
+#include
+
+#include
+
+#include <3rdParty/GSL/include/gsl/pointers>
+
+namespace CleanStart
+{
+class Module: public Py::ExtensionModule
+{
+public:
+ Module()
+ : Py::ExtensionModule("CleanStart")
+ {
+ initialize("This module is the CleanStart module."); // register with Python
+ }
+};
+
+PyObject* initModule()
+{
+ auto newModule = gsl::owner(new Module);
+ return Base::Interpreter().addModule(newModule); // Transfer ownership
+}
+
+} // namespace CleanStart
+
+/* Python entry */
+PyMOD_INIT_FUNC(CleanStart)
+{
+ PyObject* mod = CleanStart::initModule();
+ Base::Console().Log("Loading CleanStart module... done\n");
+ PyMOD_Return(mod);
+}
diff --git a/src/Mod/CleanStart/App/CMakeLists.txt b/src/Mod/CleanStart/App/CMakeLists.txt
new file mode 100644
index 0000000000..40dd253fb8
--- /dev/null
+++ b/src/Mod/CleanStart/App/CMakeLists.txt
@@ -0,0 +1,50 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# /****************************************************************************
+# * *
+# * Copyright (c) 2024 The FreeCAD Project Association AISBL *
+# * *
+# * This file is part of FreeCAD. *
+# * *
+# * FreeCAD is free software: you can redistribute it and/or modify it *
+# * under the terms of the GNU Lesser General Public License as *
+# * published by the Free Software Foundation, either version 2.1 of the *
+# * License, or (at your option) any later version. *
+# * *
+# * FreeCAD 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 *
+# * Lesser General Public License for more details. *
+# * *
+# * You should have received a copy of the GNU Lesser General Public *
+# * License along with FreeCAD. If not, see *
+# * . *
+# * *
+# ***************************************************************************/
+
+include_directories(
+ ${PYTHON_INCLUDE_DIRS}
+ ${QtCore_INCLUDE_DIRS}
+)
+
+set(CleanStart_LIBS
+ FreeCADApp
+ )
+
+SET(CleanStart_SRCS
+ AppCleanStart.cpp
+ DisplayedFilesModel.cpp
+ DisplayedFilesModel.h
+ ExamplesModel.cpp
+ ExamplesModel.h
+ PreCompiled.cpp
+ PreCompiled.h
+ RecentFilesModel.cpp
+ RecentFilesModel.h)
+
+add_library(CleanStart SHARED ${CleanStart_SRCS})
+target_link_libraries(CleanStart ${CleanStart_LIBS})
+
+SET_BIN_DIR(CleanStart CleanStart /Mod/CleanStart)
+SET_PYTHON_PREFIX_SUFFIX(CleanStart)
+
+INSTALL(TARGETS CleanStart DESTINATION ${CMAKE_INSTALL_LIBDIR})
diff --git a/src/Mod/CleanStart/App/DisplayedFilesModel.cpp b/src/Mod/CleanStart/App/DisplayedFilesModel.cpp
new file mode 100644
index 0000000000..1a91db8819
--- /dev/null
+++ b/src/Mod/CleanStart/App/DisplayedFilesModel.cpp
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/****************************************************************************
+ * *
+ * Copyright (c) 2024 The FreeCAD Project Association AISBL *
+ * *
+ * This file is part of FreeCAD. *
+ * *
+ * FreeCAD is free software: you can redistribute it and/or modify it *
+ * under the terms of the GNU Lesser General Public License as *
+ * published by the Free Software Foundation, either version 2.1 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * FreeCAD 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 *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with FreeCAD. If not, see *
+ * . *
+ * *
+ ***************************************************************************/
+
+#include "PreCompiled.h"
+#ifndef _PreComp_
+#include
+#include
+#include
+#endif
+
+#include "DisplayedFilesModel.h"
+#include
+#include
+
+using namespace CleanStart;
+
+
+namespace
+{
+
+std::string humanReadableSize(unsigned int bytes)
+{
+ static const std::vector siPrefix {
+ "b",
+ "kb",
+ "Mb",
+ "Gb",
+ "Tb",
+ "Pb",
+ "Eb" // I think it's safe to stop here (for the time being)...
+ };
+ size_t base = 0;
+ double inUnits = bytes;
+ constexpr double siFactor {1000.0};
+ while (inUnits > siFactor && base < siPrefix.size() - 1) {
+ ++base;
+ inUnits /= siFactor;
+ }
+ if (base == 0) {
+ // Don't include a decimal point for bytes
+ return fmt::format("{:.0f} {}", inUnits, siPrefix[base]);
+ }
+ // For all others, include one digit after the decimal place
+ return fmt::format("{:.1f} {}", inUnits, siPrefix[base]);
+}
+
+FileStats fileInfoFromFreeCADFile(const std::string& path)
+{
+ App::ProjectFile proj(path);
+ proj.loadDocument();
+ auto metadata = proj.getMetadata();
+ FileStats result;
+ result.insert(std::make_pair(DisplayedFilesModelRoles::author, metadata.createdBy));
+ result.insert(std::make_pair(DisplayedFilesModelRoles::modifiedTime, metadata.lastModifiedDate));
+ result.insert(std::make_pair(DisplayedFilesModelRoles::creationTime, metadata.creationDate));
+ result.insert(std::make_pair(DisplayedFilesModelRoles::company, metadata.company));
+ result.insert(std::make_pair(DisplayedFilesModelRoles::license, metadata.license));
+ result.insert(std::make_pair(DisplayedFilesModelRoles::description, metadata.comment));
+ return result;
+}
+
+/// Load the thumbnail image data (if any) that is stored in an FCStd file.
+/// \returns The image bytes, or an empty QByteArray (if no thumbnail was stored)
+QByteArray loadFCStdThumbnail(const std::string & pathToFCStdFile)
+{
+ App::ProjectFile proj(pathToFCStdFile);
+ if (proj.loadDocument()) {
+ try {
+ std::string thumbnailFile = proj.extractInputFile("thumbnails/Thumbnail.png");
+ auto inputFile = QFile(QString::fromStdString(thumbnailFile));
+ inputFile.open(QIODevice::OpenModeFlag::ReadOnly);
+ return inputFile.readAll();
+ }
+ catch (...) {
+ }
+ }
+ return {};
+}
+
+FileStats getFileInfo(const std::string& path)
+{
+ FileStats result;
+ Base::FileInfo file(path);
+ if (file.hasExtension("FCStd")) {
+ result = fileInfoFromFreeCADFile(path);
+ }
+ else {
+ file.lastModified();
+ }
+ result.insert(std::make_pair(DisplayedFilesModelRoles::path, path));
+ result.insert(std::make_pair(DisplayedFilesModelRoles::size, humanReadableSize(file.size())));
+ result.insert(std::make_pair(DisplayedFilesModelRoles::baseName, file.fileName()));
+ return result;
+}
+} // namespace
+
+DisplayedFilesModel::DisplayedFilesModel(QObject* parent) : QAbstractListModel(parent)
+{
+}
+
+
+int DisplayedFilesModel::rowCount(const QModelIndex& parent) const
+{
+ Q_UNUSED(parent);
+ return static_cast(_fileInfoCache.size());
+}
+
+QVariant DisplayedFilesModel::data(const QModelIndex& index, int roleAsInt) const
+{
+ int row = index.row();
+ if (row < 0 || row >= static_cast(_fileInfoCache.size())) {
+ return {};
+ }
+ auto mapEntry = _fileInfoCache.at(row);
+ auto role = static_cast(roleAsInt);
+ switch (role) {
+ case DisplayedFilesModelRoles::author: // NOLINT(bugprone-branch-clone)
+ [[fallthrough]];
+ case DisplayedFilesModelRoles::baseName:
+ [[fallthrough]];
+ case DisplayedFilesModelRoles::company:
+ [[fallthrough]];
+ case DisplayedFilesModelRoles::creationTime:
+ [[fallthrough]];
+ case DisplayedFilesModelRoles::description:
+ [[fallthrough]];
+ case DisplayedFilesModelRoles::license:
+ [[fallthrough]];
+ case DisplayedFilesModelRoles::modifiedTime:
+ [[fallthrough]];
+ case DisplayedFilesModelRoles::path:
+ [[fallthrough]];
+ case DisplayedFilesModelRoles::size:
+ if (mapEntry.find(role) != mapEntry.end()) {
+ return QString::fromStdString(mapEntry.at(role));
+ }
+ else {
+ return {};
+ }
+ case DisplayedFilesModelRoles::image: {
+ auto path = QString::fromStdString(mapEntry.at(DisplayedFilesModelRoles::path));
+ if (_imageCache.contains(path)) {
+ return _imageCache[path];
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ switch (roleAsInt) {
+ case Qt::ItemDataRole::ToolTipRole:
+ return QString::fromStdString(mapEntry.at(DisplayedFilesModelRoles::path));
+ }
+ return {};
+}
+
+bool freecadCanOpen (const QString& extension)
+{
+ auto importTypes = App::GetApplication().getImportTypes();
+ return std::find(importTypes.begin(), importTypes.end(), extension.toStdString()) != importTypes.end();
+}
+
+void DisplayedFilesModel::addFile(const QString &filePath)
+{
+ QFileInfo qfi (filePath);
+ if (!qfi.isReadable()){
+ return;
+ }
+ if (!freecadCanOpen(qfi.suffix())) {
+ return;
+ }
+ _fileInfoCache.emplace_back(getFileInfo(filePath.toStdString()));
+ if (qfi.completeSuffix() == QLatin1String("FCStd")) {
+ auto thumbnail = loadFCStdThumbnail(filePath.toStdString());
+ if (!thumbnail.isEmpty()) {
+ _imageCache.insert(filePath, thumbnail);
+ }
+ }
+}
+
+void DisplayedFilesModel::clear()
+{
+ _fileInfoCache.clear();
+}
+
+QHash DisplayedFilesModel::roleNames() const
+{
+ static QHash nameMap {
+ std::make_pair(int(DisplayedFilesModelRoles::author), "author"),
+ std::make_pair(int(DisplayedFilesModelRoles::baseName), "baseName"),
+ std::make_pair(int(DisplayedFilesModelRoles::company), "company"),
+ std::make_pair(int(DisplayedFilesModelRoles::creationTime), "creationTime"),
+ std::make_pair(int(DisplayedFilesModelRoles::description), "description"),
+ std::make_pair(int(DisplayedFilesModelRoles::image), "image"),
+ std::make_pair(int(DisplayedFilesModelRoles::license), "license"),
+ std::make_pair(int(DisplayedFilesModelRoles::modifiedTime), "modifiedTime"),
+ std::make_pair(int(DisplayedFilesModelRoles::path), "path"),
+ std::make_pair(int(DisplayedFilesModelRoles::size), "size"),
+ };
+ return nameMap;
+}
diff --git a/src/Mod/CleanStart/App/DisplayedFilesModel.h b/src/Mod/CleanStart/App/DisplayedFilesModel.h
new file mode 100644
index 0000000000..e08937d060
--- /dev/null
+++ b/src/Mod/CleanStart/App/DisplayedFilesModel.h
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/****************************************************************************
+ * *
+ * Copyright (c) 2024 The FreeCAD Project Association AISBL *
+ * *
+ * This file is part of FreeCAD. *
+ * *
+ * FreeCAD is free software: you can redistribute it and/or modify it *
+ * under the terms of the GNU Lesser General Public License as *
+ * published by the Free Software Foundation, either version 2.1 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * FreeCAD 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 *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with FreeCAD. If not, see *
+ * . *
+ * *
+ ***************************************************************************/
+
+#ifndef FREECAD_CLEANSTART_DISPLAYEDFILESMODEL_H
+#define FREECAD_CLEANSTART_DISPLAYEDFILESMODEL_H
+
+#include
+#include
+#include
+
+#include "../CleanStartGlobal.h"
+
+namespace CleanStart
+{
+
+enum class DisplayedFilesModelRoles
+{
+ baseName = Qt::UserRole + 1,
+ image,
+ size,
+ author,
+ creationTime,
+ modifiedTime,
+ description,
+ company,
+ license,
+ path
+};
+
+using FileStats = std::map;
+
+/// A model for displaying a list of files including a thumbnail or icon, plus various file statistics.
+class CleanStartExport DisplayedFilesModel: public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ explicit DisplayedFilesModel(QObject* parent = nullptr);
+
+ int rowCount(const QModelIndex& parent = QModelIndex()) const override;
+
+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
+
+ void addFile(const QString &filePath);
+
+ void clear();
+
+protected:
+ /// For communication with QML, define the text version of each role name defined in the
+ /// DisplayedFilesModelRoles enumeration
+ QHash roleNames() const override;
+
+ /// Destroy and recreate the cache of info about the files. Should be connected to a signal
+ /// indicating when some piece of information about the files has changed. Does NOT generate
+ /// a new list of files, only re-caches the existing ones.
+ void reCacheFileInfo();
+
+private:
+
+ std::vector _fileInfoCache;
+ QMap _imageCache;
+};
+
+} // namespace CleanStart
+
+#endif // FREECAD_CLEANSTART_DISPLAYEDFILESMODEL_H
diff --git a/src/Mod/CleanStart/App/ExamplesModel.cpp b/src/Mod/CleanStart/App/ExamplesModel.cpp
new file mode 100644
index 0000000000..7b9733158b
--- /dev/null
+++ b/src/Mod/CleanStart/App/ExamplesModel.cpp
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/****************************************************************************
+ * *
+ * Copyright (c) 2024 The FreeCAD Project Association AISBL *
+ * *
+ * This file is part of FreeCAD. *
+ * *
+ * FreeCAD is free software: you can redistribute it and/or modify it *
+ * under the terms of the GNU Lesser General Public License as *
+ * published by the Free Software Foundation, either version 2.1 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * FreeCAD 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 *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with FreeCAD. If not, see *
+ * . *
+ * *
+ ***************************************************************************/
+
+#include "PreCompiled.h"
+#ifndef _PreComp_
+#include
+#endif
+
+#include "ExamplesModel.h"
+#include
+
+using namespace CleanStart;
+
+FC_LOG_LEVEL_INIT(ExamplesModel)
+
+ExamplesModel::ExamplesModel(QObject* parent) : DisplayedFilesModel(parent)
+{
+ auto examplesPath = QDir(QString::fromStdString(App::Application::getResourceDir()));
+ _examplesDirectory.setPath (examplesPath.filePath(QLatin1String("examples")));
+}
+
+void ExamplesModel::loadExamples()
+{
+ beginResetModel();
+ clear();
+ if (!_examplesDirectory.isReadable()) {
+ Base::Console().Warning("Cannot read %s", _examplesDirectory.absolutePath().toStdString().c_str());
+ }
+ auto entries = _examplesDirectory.entryList(QDir::Filter::Files | QDir::Filter::Readable, QDir::SortFlag::Name);
+ for (const auto & entry : entries) {
+ addFile(_examplesDirectory.filePath(entry));
+ }
+ endResetModel();
+}
diff --git a/src/Mod/CleanStart/App/ExamplesModel.h b/src/Mod/CleanStart/App/ExamplesModel.h
new file mode 100644
index 0000000000..fe232b737a
--- /dev/null
+++ b/src/Mod/CleanStart/App/ExamplesModel.h
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/****************************************************************************
+ * *
+ * Copyright (c) 2024 The FreeCAD Project Association AISBL *
+ * *
+ * This file is part of FreeCAD. *
+ * *
+ * FreeCAD is free software: you can redistribute it and/or modify it *
+ * under the terms of the GNU Lesser General Public License as *
+ * published by the Free Software Foundation, either version 2.1 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * FreeCAD 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 *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with FreeCAD. If not, see *
+ * . *
+ * *
+ ***************************************************************************/
+
+#ifndef FREECAD_CLEANSTART_EXAMPLESMODEL_H
+#define FREECAD_CLEANSTART_EXAMPLESMODEL_H
+
+#include
+#include
+#include
+#include
+
+#include "DisplayedFilesModel.h"
+#include "../CleanStartGlobal.h"
+
+namespace CleanStart
+{
+
+/// A model for displaying a list of files including a thumbnail or icon, plus various file statistics.
+class CleanStartExport ExamplesModel: public DisplayedFilesModel
+{
+ Q_OBJECT
+public:
+ explicit ExamplesModel(QObject* parent = nullptr);
+
+ void loadExamples();
+
+private:
+ QDir _examplesDirectory;
+};
+
+} // namespace CleanStart
+
+#endif // FREECAD_CLEANSTART_EXAMPLESMODEL_H
diff --git a/src/Mod/CleanStart/App/PreCompiled.cpp b/src/Mod/CleanStart/App/PreCompiled.cpp
new file mode 100644
index 0000000000..b17d7731bd
--- /dev/null
+++ b/src/Mod/CleanStart/App/PreCompiled.cpp
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/****************************************************************************
+ * *
+# Copyright (c) 2024 The FreeCAD Project Association AISBL *
+ * *
+ * This file is part of FreeCAD. *
+ * *
+ * FreeCAD is free software: you can redistribute it and/or modify it *
+ * under the terms of the GNU Lesser General Public License as *
+ * published by the Free Software Foundation, either version 2.1 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * FreeCAD 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 *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with FreeCAD. If not, see *
+ * . *
+ * *
+ ***************************************************************************/
+
+
+#include "PreCompiled.h"
diff --git a/src/Mod/CleanStart/App/PreCompiled.h b/src/Mod/CleanStart/App/PreCompiled.h
new file mode 100644
index 0000000000..711517255f
--- /dev/null
+++ b/src/Mod/CleanStart/App/PreCompiled.h
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/****************************************************************************
+ * *
+# Copyright (c) 2024 The FreeCAD Project Association AISBL *
+ * *
+ * This file is part of FreeCAD. *
+ * *
+ * FreeCAD is free software: you can redistribute it and/or modify it *
+ * under the terms of the GNU Lesser General Public License as *
+ * published by the Free Software Foundation, either version 2.1 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * FreeCAD 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 *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with FreeCAD. If not, see *
+ * . *
+ * *
+ ***************************************************************************/
+
+#ifndef CLEANSTART_PRECOMPILED_H
+#define CLEANSTART_PRECOMPILED_H
+
+#include
+
+#ifdef _MSC_VER
+#pragma warning(disable : 5208)
+#endif
+
+#ifdef _PreComp_
+
+// standard
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+// Qt (should never include GUI files, only QtCore)
+#include
+#include
+#include
+
+#endif // _PreComp_
+#endif // CLEANSTART_PRECOMPILED_H
diff --git a/src/Mod/CleanStart/App/RecentFilesModel.cpp b/src/Mod/CleanStart/App/RecentFilesModel.cpp
new file mode 100644
index 0000000000..238007a368
--- /dev/null
+++ b/src/Mod/CleanStart/App/RecentFilesModel.cpp
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/****************************************************************************
+ * *
+ * Copyright (c) 2024 The FreeCAD Project Association AISBL *
+ * *
+ * This file is part of FreeCAD. *
+ * *
+ * FreeCAD is free software: you can redistribute it and/or modify it *
+ * under the terms of the GNU Lesser General Public License as *
+ * published by the Free Software Foundation, either version 2.1 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * FreeCAD 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 *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with FreeCAD. If not, see *
+ * . *
+ * *
+ ***************************************************************************/
+
+#include "PreCompiled.h"
+#ifndef _PreComp_
+#endif
+
+#include "RecentFilesModel.h"
+#include
+#include
+
+using namespace CleanStart;
+
+RecentFilesModel::RecentFilesModel(QObject* parent) : DisplayedFilesModel(parent)
+{
+ _parameterGroup = App::GetApplication().GetParameterGroupByPath(
+ "User parameter:BaseApp/Preferences/RecentFiles");
+}
+
+void RecentFilesModel::loadRecentFiles()
+{
+ beginResetModel();
+ clear();
+ auto numRows {_parameterGroup->GetInt("RecentFiles", 0)};
+ for (int i = 0; i < numRows; ++i) {
+ auto entry = fmt::format("MRU{}", i);
+ auto path = _parameterGroup->GetASCII(entry.c_str(), "");
+ addFile(QString::fromStdString(path));
+ }
+ endResetModel();
+}
diff --git a/src/Mod/CleanStart/App/RecentFilesModel.h b/src/Mod/CleanStart/App/RecentFilesModel.h
new file mode 100644
index 0000000000..077c53c54b
--- /dev/null
+++ b/src/Mod/CleanStart/App/RecentFilesModel.h
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/****************************************************************************
+ * *
+ * Copyright (c) 2024 The FreeCAD Project Association AISBL *
+ * *
+ * This file is part of FreeCAD. *
+ * *
+ * FreeCAD is free software: you can redistribute it and/or modify it *
+ * under the terms of the GNU Lesser General Public License as *
+ * published by the Free Software Foundation, either version 2.1 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * FreeCAD 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 *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with FreeCAD. If not, see *
+ * . *
+ * *
+ ***************************************************************************/
+
+#ifndef FREECAD_CLEANSTART_RECENTFILESMODEL_H
+#define FREECAD_CLEANSTART_RECENTFILESMODEL_H
+
+#include
+#include
+#include
+
+#include "DisplayedFilesModel.h"
+#include "../CleanStartGlobal.h"
+
+namespace CleanStart
+{
+
+/// A model for displaying a list of files including a thumbnail or icon, plus various file statistics.
+class CleanStartExport RecentFilesModel: public DisplayedFilesModel
+{
+ Q_OBJECT
+public:
+ explicit RecentFilesModel(QObject* parent = nullptr);
+
+ void loadRecentFiles();
+
+private:
+ Base::Reference _parameterGroup;
+};
+
+} // namespace CleanStart
+
+#endif // FREECAD_CLEANSTART_RECENTFILESMODEL_H
diff --git a/src/Mod/CleanStart/CMakeLists.txt b/src/Mod/CleanStart/CMakeLists.txt
new file mode 100644
index 0000000000..7f6a38c845
--- /dev/null
+++ b/src/Mod/CleanStart/CMakeLists.txt
@@ -0,0 +1,49 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# /****************************************************************************
+# * *
+# * Copyright (c) 2024 The FreeCAD Project Association AISBL *
+# * *
+# * This file is part of FreeCAD. *
+# * *
+# * FreeCAD is free software: you can redistribute it and/or modify it *
+# * under the terms of the GNU Lesser General Public License as *
+# * published by the Free Software Foundation, either version 2.1 of the *
+# * License, or (at your option) any later version. *
+# * *
+# * FreeCAD 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 *
+# * Lesser General Public License for more details. *
+# * *
+# * You should have received a copy of the GNU Lesser General Public *
+# * License along with FreeCAD. If not, see *
+# * . *
+# * *
+# ***************************************************************************/
+
+add_subdirectory(App)
+
+set(CleanStart_Scripts
+ Init.py
+ )
+
+if (BUILD_GUI)
+ add_subdirectory(Gui)
+ list(APPEND CleanStart_Scripts InitGui.py)
+endif (BUILD_GUI)
+
+add_custom_target(CleanStartScripts ALL
+ SOURCES ${CleanStart_Scripts}
+ )
+
+fc_target_copy_resource(CleanStartScripts
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_BINARY_DIR}/Mod/CleanStart
+ ${CleanStart_Scripts})
+
+INSTALL(
+ FILES
+ ${CleanStart_Scripts}
+ DESTINATION
+ Mod/CleanStart
+)
diff --git a/src/Mod/CleanStart/CleanStartGlobal.h b/src/Mod/CleanStart/CleanStartGlobal.h
new file mode 100644
index 0000000000..c4d367d858
--- /dev/null
+++ b/src/Mod/CleanStart/CleanStartGlobal.h
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/****************************************************************************
+ * *
+ * Copyright (c) 2024 The FreeCAD Project Association AISBL *
+ * *
+ * This file is part of FreeCAD. *
+ * *
+ * FreeCAD is free software: you can redistribute it and/or modify it *
+ * under the terms of the GNU Lesser General Public License as *
+ * published by the Free Software Foundation, either version 2.1 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * FreeCAD 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 *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with FreeCAD. If not, see *
+ * . *
+ * *
+ ***************************************************************************/
+
+#include
+
+#ifndef LAUNCHER_GLOBAL_H
+#define LAUNCHER_GLOBAL_H
+
+
+// CleanStart
+#ifndef CleanStartExport
+#ifdef CleanStart_EXPORTS
+#define CleanStartExport FREECAD_DECL_EXPORT
+#else
+#define CleanStartExport FREECAD_DECL_IMPORT
+#endif
+#endif
+
+// CleanStartGui
+#ifndef CleanStartGuiExport
+#ifdef CleanStartGui_EXPORTS
+#define CleanStartGuiExport FREECAD_DECL_EXPORT
+#else
+#define CleanStartGuiExport FREECAD_DECL_IMPORT
+#endif
+#endif
+
+#endif // LAUNCHER_GLOBAL_H
diff --git a/src/Mod/CleanStart/Gui/AppCleanStartGui.cpp b/src/Mod/CleanStart/Gui/AppCleanStartGui.cpp
new file mode 100644
index 0000000000..47dbb42dd3
--- /dev/null
+++ b/src/Mod/CleanStart/Gui/AppCleanStartGui.cpp
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/****************************************************************************
+ * *
+ * Copyright (c) 2024 The FreeCAD Project Association AISBL *
+ * *
+ * This file is part of FreeCAD. *
+ * *
+ * FreeCAD is free software: you can redistribute it and/or modify it *
+ * under the terms of the GNU Lesser General Public License as *
+ * published by the Free Software Foundation, either version 2.1 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * FreeCAD 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 *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with FreeCAD. If not, see *
+ * . *
+ * *
+ ***************************************************************************/
+
+#include "PreCompiled.h"
+
+#include
+#include
+#include
+#include
+
+#include
+
+#include <3rdParty/GSL/include/gsl/pointers>
+
+#include "Workbench.h"
+
+void loadCleanStartResource()
+{
+ // add resources and reloads the translators
+ Q_INIT_RESOURCE(CleanStart);
+ Q_INIT_RESOURCE(CleanStart_translation);
+ Gui::Translator::instance()->refresh();
+}
+
+namespace CleanStartGui
+{
+ extern PyObject* initModule();
+}
+
+
+namespace CleanStartGui
+{
+class Module: public Py::ExtensionModule
+{
+public:
+ Module()
+ : Py::ExtensionModule("CleanStartGui")
+ {
+ initialize("This module is the CleanStartGui module."); // register with Python
+ }
+};
+
+PyObject* initModule()
+{
+ auto newModule = gsl::owner(new Module);
+ return Base::Interpreter().addModule(newModule); // Transfer ownership
+}
+
+} // namespace CleanStartGui
+
+/* Python entry */
+PyMOD_INIT_FUNC(CleanStartGui)
+{
+ Base::Console().Log("Loading GUI of CleanStart module... ");
+ PyObject* mod = CleanStartGui::initModule();
+ CleanStartGui::Workbench::init();
+ loadCleanStartResource();
+ Base::Console().Log("done\n");
+
+ PyMOD_Return(mod);
+}
diff --git a/src/Mod/CleanStart/Gui/CMakeLists.txt b/src/Mod/CleanStart/Gui/CMakeLists.txt
new file mode 100644
index 0000000000..01a7b02b4f
--- /dev/null
+++ b/src/Mod/CleanStart/Gui/CMakeLists.txt
@@ -0,0 +1,89 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# /****************************************************************************
+# * *
+# * Copyright (c) 2024 The FreeCAD Project Association AISBL *
+# * *
+# * This file is part of FreeCAD. *
+# * *
+# * FreeCAD is free software: you can redistribute it and/or modify it *
+# * under the terms of the GNU Lesser General Public License as *
+# * published by the Free Software Foundation, either version 2.1 of the *
+# * License, or (at your option) any later version. *
+# * *
+# * FreeCAD 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 *
+# * Lesser General Public License for more details. *
+# * *
+# * You should have received a copy of the GNU Lesser General Public *
+# * License along with FreeCAD. If not, see *
+# * . *
+# * *
+# ***************************************************************************/
+
+include_directories(
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${PYTHON_INCLUDE_DIRS}
+ ${Boost_INCLUDE_DIRS}
+ ${ZLIB_INCLUDE_DIR}
+ ${XercesC_INCLUDE_DIRS}
+ ${QtCore_INCLUDE_DIRS}
+ ${QtWidgets_INCLUDE_DIRS}
+ ${QtSvg_INCLUDE_DIRS}
+ #${QtNetwork_INCLUDE_DIRS}
+ ${QtUiTools_INCLUDE_DIRS}
+ #${QtQuick_INCLUDE_DIRS}
+ #${QtQuickWidgets_INCLUDE_DIRS}
+)
+
+set(CleanStartGui_LIBS
+ CleanStart
+ FreeCADGui
+ )
+
+set(CleanStart_TR_QRC ${CMAKE_CURRENT_BINARY_DIR}/Resources/CleanStart_translation.qrc)
+qt_find_and_add_translation(QM_SRCS "Resources/translations/*_*.ts"
+ ${CMAKE_CURRENT_BINARY_DIR}/Resources/translations)
+qt_create_resource_file(${CleanStart_TR_QRC} ${QM_SRCS})
+qt_add_resources(CleanStart_QRC_SRCS Resources/CleanStart.qrc ${CleanStart_TR_QRC})
+# qtquick_compiler_add_resources(CleanStart_QRC_SRCS Resources/CleanStart.qrc ${CleanStart_TR_QRC} qml.qrc)
+
+SET(CleanStartGui_SRCS
+ ${CleanStart_QRC_SRCS}
+ ${CleanStartGui_UIC_SRCS}
+ AppCleanStartGui.cpp
+ Command.cpp
+ PreCompiled.cpp
+ PreCompiled.h
+ CleanStartView.cpp
+ CleanStartView.h
+ FileCardDelegate.cpp
+ FileCardDelegate.h
+ FileCardView.cpp
+ FileCardView.h
+ Workbench.cpp
+ Workbench.h
+ )
+
+SET(CleanStartGuiIcon_SVG
+ Resources/icons/CleanStartWorkbench.svg
+ )
+
+# TODO: Evaluate PCH use with Qt6/QtQuick/Qml
+if (FREECAD_USE_PCH)
+ add_definitions(-D_PreComp_)
+ GET_MSVC_PRECOMPILED_SOURCE("PreCompiled.cpp" PCH_SRCS ${CleanStartGui_SRCS})
+ ADD_MSVC_PRECOMPILED_HEADER(CleanStartGui PreCompiled.h PreCompiled.cpp PCH_SRCS)
+endif (FREECAD_USE_PCH)
+
+add_library(CleanStartGui SHARED ${CleanStartGui_SRCS} ${CleanStartGuiIcon_SVG})
+# target_link_libraries(CleanStartGui ${CleanStartGui_LIBS} Qt::Quick Qt::Qml Qt::QuickWidgets)
+target_link_libraries(CleanStartGui ${CleanStartGui_LIBS})
+
+SET_BIN_DIR(CleanStartGui CleanStartGui /Mod/CleanStart)
+SET_PYTHON_PREFIX_SUFFIX(CleanStartGui)
+
+fc_copy_sources(CleanStartGui "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_DATADIR}/Mod/CleanStart" ${CleanStartGuiIcon_SVG})
+
+INSTALL(TARGETS CleanStartGui DESTINATION ${CMAKE_INSTALL_LIBDIR})
+INSTALL(FILES ${CleanStartGuiIcon_SVG} DESTINATION "${CMAKE_INSTALL_DATADIR}/Mod/CleanStart/Resources/icons")
diff --git a/src/Mod/CleanStart/Gui/CleanStartView.cpp b/src/Mod/CleanStart/Gui/CleanStartView.cpp
new file mode 100644
index 0000000000..24dfb366e4
--- /dev/null
+++ b/src/Mod/CleanStart/Gui/CleanStartView.cpp
@@ -0,0 +1,298 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/****************************************************************************
+ * *
+ * Copyright (c) 2024 The FreeCAD Project Association AISBL *
+ * *
+ * This file is part of FreeCAD. *
+ * *
+ * FreeCAD is free software: you can redistribute it and/or modify it *
+ * under the terms of the GNU Lesser General Public License as *
+ * published by the Free Software Foundation, either version 2.1 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * FreeCAD 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 *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with FreeCAD. If not, see *
+ * . *
+ * *
+ ***************************************************************************/
+
+#include "PreCompiled.h"
+#ifndef _PreComp_
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#endif
+
+#include "CleanStartView.h"
+#include "FileCardDelegate.h"
+#include "FileCardView.h"
+#include "Gui/Workbench.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include <3rdParty/GSL/include/gsl/pointers>
+
+using namespace CleanStartGui;
+
+TYPESYSTEM_SOURCE_ABSTRACT(CleanStartGui::CleanStartView, Gui::MDIView) // NOLINT
+
+namespace {
+
+struct NewButton
+{
+ QString heading;
+ QString description;
+ QString iconPath;
+};
+
+gsl::owner createNewButton(const NewButton& newButton)
+{
+ auto hGrp = App::GetApplication().GetParameterGroupByPath(
+ "User parameter:BaseApp/Preferences/Mod/Start");
+ const auto cardSpacing = static_cast(hGrp->GetInt("FileCardSpacing", 20)); // NOLINT
+ const auto newFileIconSize = static_cast(hGrp->GetInt("NewFileIconSize", 48)); // NOLINT
+
+ auto button = gsl::owner(new QPushButton());
+ auto mainLayout = gsl::owner(new QHBoxLayout(button));
+ auto iconLabel = gsl::owner(new QLabel(button));
+ mainLayout->addWidget(iconLabel);
+ QIcon baseIcon(newButton.iconPath);
+ iconLabel->setPixmap(baseIcon.pixmap(newFileIconSize, newFileIconSize));
+
+ auto textLayout = gsl::owner(new QVBoxLayout);
+ auto textLabelLine1 = gsl::owner(new QLabel(button));
+ textLabelLine1->setText(QLatin1String("") + newButton.heading + QLatin1String(" "));
+ auto textLabelLine2 = gsl::owner(new QLabel(button));
+ textLabelLine2->setText(newButton.description);
+ textLabelLine2->setWordWrap(true);
+ textLayout->addWidget(textLabelLine1);
+ textLayout->addWidget(textLabelLine2);
+ textLayout->setSpacing(0);
+ mainLayout->addItem(textLayout);
+
+ mainLayout->addStretch();
+
+ button->setMinimumHeight(newFileIconSize + cardSpacing);
+ return button;
+}
+
+}
+
+CleanStartView::CleanStartView(Gui::Document* pcDocument, QWidget* parent)
+ : Gui::MDIView(pcDocument, parent)
+ , _contents(new QScrollArea(parent))
+{
+ setObjectName(QLatin1String("CleanStartView"));
+ auto hGrp = App::GetApplication().GetParameterGroupByPath(
+ "User parameter:BaseApp/Preferences/Mod/Start");
+ auto cardSpacing = hGrp->GetInt("FileCardSpacing", 20); // NOLINT
+
+ auto scrolledWidget = gsl::owner(new QWidget(this));
+ _contents->setWidget(scrolledWidget);
+ _contents->setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff);
+ _contents->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded);
+ _contents->setWidgetResizable(true);
+ auto layout = gsl::owner(new QVBoxLayout(scrolledWidget));
+ layout->setSizeConstraint(QLayout::SizeConstraint::SetMinAndMaxSize);
+
+ auto newFileLabel = gsl::owner(new QLabel(tr("New File")));
+ layout->addWidget(newFileLabel);
+ auto gridLayout = gsl::owner(new QGridLayout);
+ layout->addLayout(gridLayout);
+ configureNewFileButtons(gridLayout);
+
+ auto recentFilesLabel = gsl::owner(new QLabel(tr("Recent Files")));
+ layout->addWidget(recentFilesLabel);
+ auto recentFilesListWidget = gsl::owner(new FileCardView(_contents));
+ connect(recentFilesListWidget, &QListView::clicked, this, &CleanStartView::fileCardSelected);
+ layout->addWidget(recentFilesListWidget);
+
+ auto examplesLabel = gsl::owner(new QLabel(tr("Examples")));
+ layout->addWidget(examplesLabel);
+ auto examplesListWidget = gsl::owner(new FileCardView(_contents));
+ connect(examplesListWidget, &QListView::clicked, this, &CleanStartView::fileCardSelected);
+ layout->addWidget(examplesListWidget);
+
+ layout->setSpacing(static_cast(cardSpacing));
+ layout->addStretch();
+
+ setCentralWidget(_contents);
+
+ QString title = QCoreApplication::translate("Workbench", "Start");
+ setWindowTitle(title);
+
+ configureExamplesListWidget(examplesListWidget);
+ configureRecentFilesListWidget(recentFilesListWidget);
+}
+
+
+void CleanStartView::configureNewFileButtons(QGridLayout* layout) const
+{
+ auto newEmptyFile = createNewButton({tr("Empty file"),
+ tr("Create a new empty FreeCAD file"),
+ QLatin1String(":/icons/document-new.svg")});
+ auto openFile = createNewButton({tr("Open File"),
+ tr("Open an existing CAD file or 3D model"),
+ QLatin1String(":/icons/document-open.svg")});
+ auto partDesign = createNewButton({tr("Parametric Part"),
+ tr("Create a part with the Part Design workbench"),
+ QLatin1String(":/icons/PartDesignWorkbench.svg")});
+ auto assembly = createNewButton({tr("Assembly"),
+ tr("Create an assembly project"),
+ QLatin1String(":/icons/AssemblyWorkbench.svg")});
+ auto draft = createNewButton({tr("2D Draft"),
+ tr("Create a 2D Draft with the Draft workbench"),
+ QLatin1String(":/icons/DraftWorkbench.svg")});
+ auto arch = createNewButton({tr("BIM/Architecture"),
+ tr("Create an architectural project"),
+ QLatin1String(":/icons/ArchWorkbench.svg")});
+
+ layout->addWidget(partDesign, 0, 0);
+ layout->addWidget(assembly, 0, 1);
+ layout->addWidget(draft, 0, 2);
+ layout->addWidget(arch, 1, 0);
+ layout->addWidget(newEmptyFile, 1, 1);
+ layout->addWidget(openFile, 1, 2);
+
+ connect(newEmptyFile, &QPushButton::clicked, this, &CleanStartView::newEmptyFile);
+ connect(openFile, &QPushButton::clicked, this, &CleanStartView::openExistingFile);
+ connect(partDesign, &QPushButton::clicked, this, &CleanStartView::newPartDesignFile);
+ connect(assembly, &QPushButton::clicked, this, &CleanStartView::newAssemblyFile);
+ connect(draft, &QPushButton::clicked, this, &CleanStartView::newDraftFile);
+ connect(arch, &QPushButton::clicked, this, &CleanStartView::newArchFile);
+}
+
+void CleanStartView::configureFileCardWidget(QListView* fileCardWidget)
+{
+ auto delegate = gsl::owner(new FileCardDelegate);
+ fileCardWidget->setItemDelegate(delegate);
+ fileCardWidget->setMinimumWidth(fileCardWidget->parentWidget()->width());
+ fileCardWidget->setGridSize(
+ fileCardWidget->itemDelegate()->sizeHint(QStyleOptionViewItem(),
+ fileCardWidget->model()->index(0, 0)));
+}
+
+
+void CleanStartView::configureRecentFilesListWidget(QListView* recentFilesListWidget)
+{
+ _recentFilesModel.loadRecentFiles();
+ recentFilesListWidget->setModel(&_recentFilesModel);
+ configureFileCardWidget(recentFilesListWidget);
+}
+
+
+void CleanStartView::configureExamplesListWidget(QListView* examplesListWidget)
+{
+ _examplesModel.loadExamples();
+ examplesListWidget->setModel(&_examplesModel);
+ configureFileCardWidget(examplesListWidget);
+}
+
+
+void CleanStartView::newEmptyFile() const
+{
+ Gui::Application::Instance->commandManager().runCommandByName("Std_New");
+ postStart(PostStartBehavior::switchWorkbench);
+}
+
+void CleanStartView::newPartDesignFile() const
+{
+ Gui::Application::Instance->commandManager().runCommandByName("Std_New");
+ Gui::Application::Instance->activateWorkbench("PartDesignWorkbench");
+ Gui::Application::Instance->commandManager().runCommandByName("PartDesign_Body");
+ postStart(PostStartBehavior::doNotSwitchWorkbench);
+}
+
+void CleanStartView::openExistingFile() const
+{
+ auto originalDocument = Gui::Application::Instance->activeDocument();
+ Gui::Application::Instance->commandManager().runCommandByName("Std_Open");
+ if (Gui::Application::Instance->activeDocument() != originalDocument) {
+ // Only run this if the user chose a new document to open (that is, they didn't cancel the
+ // open file dialog)
+ postStart(PostStartBehavior::switchWorkbench);
+ }
+}
+
+void CleanStartView::newAssemblyFile() const
+{
+ Gui::Application::Instance->commandManager().runCommandByName("Std_New");
+ Gui::Application::Instance->activateWorkbench("AssemblyWorkbench");
+ Gui::Application::Instance->commandManager().runCommandByName("Assembly_CreateAssembly");
+ Gui::Application::Instance->commandManager().runCommandByName("Std_Refresh");
+ postStart(PostStartBehavior::doNotSwitchWorkbench);
+}
+
+void CleanStartView::newDraftFile() const
+{
+ Gui::Application::Instance->commandManager().runCommandByName("Std_New");
+ Gui::Application::Instance->activateWorkbench("DraftWorkbench");
+ Gui::Application::Instance->commandManager().runCommandByName("Std_ViewTop");
+ postStart(PostStartBehavior::doNotSwitchWorkbench);
+}
+
+void CleanStartView::newArchFile() const
+{
+ Gui::Application::Instance->commandManager().runCommandByName("Std_New");
+ try {
+ Gui::Application::Instance->activateWorkbench("BIMWorkbench");
+ }
+ catch (...) {
+ Gui::Application::Instance->activateWorkbench("ArchWorkbench");
+ }
+ postStart(PostStartBehavior::doNotSwitchWorkbench);
+}
+
+void CleanStartView::postStart(PostStartBehavior behavior) const
+{
+ auto hGrp = App::GetApplication().GetParameterGroupByPath(
+ "User parameter:BaseApp/Preferences/Mod/Start");
+
+ if (behavior == PostStartBehavior::switchWorkbench) {
+ auto wb = hGrp->GetASCII("AutoloadModule", "");
+ if (wb == "$LastModule") {
+ wb = App::GetApplication()
+ .GetParameterGroupByPath("User parameter:BaseApp/Preferences/General")
+ ->GetASCII("LastModule", "");
+ }
+ if (!wb.empty()) {
+ Gui::Application::Instance->activateWorkbench(wb.c_str());
+ }
+ }
+ auto closeStart = hGrp->GetBool("closeStart", false);
+ if (closeStart) {
+ this->window()->close();
+ }
+}
+
+
+void CleanStartView::fileCardSelected(const QModelIndex& index)
+{
+ auto file = index.data(static_cast(CleanStart::DisplayedFilesModelRoles::path)).toString();
+ auto command = std::string("FreeCAD.loadFile('") + file.toStdString() + "')";
+ try {
+ Base::Interpreter().runString(command.c_str());
+ postStart(PostStartBehavior::doNotSwitchWorkbench);
+ }
+ catch (Base::PyException& e) {
+ Base::Console().Error(e.getMessage().c_str());
+ }
+ catch (Base::Exception &e) {
+ Base::Console().Error(e.getMessage().c_str());
+ }
+ catch (...) {
+ Base::Console().Error("An unknown error occurred");
+ }
+}
diff --git a/src/Mod/CleanStart/Gui/CleanStartView.h b/src/Mod/CleanStart/Gui/CleanStartView.h
new file mode 100644
index 0000000000..4d33e86370
--- /dev/null
+++ b/src/Mod/CleanStart/Gui/CleanStartView.h
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/****************************************************************************
+ * *
+ * Copyright (c) 2024 The FreeCAD Project Association AISBL *
+ * *
+ * This file is part of FreeCAD. *
+ * *
+ * FreeCAD is free software: you can redistribute it and/or modify it *
+ * under the terms of the GNU Lesser General Public License as *
+ * published by the Free Software Foundation, either version 2.1 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * FreeCAD 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 *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with FreeCAD. If not, see *
+ * . *
+ * *
+ ***************************************************************************/
+
+#ifndef FREECAD_CLEANSTARTVIEW_H
+#define FREECAD_CLEANSTARTVIEW_H
+
+#include
+#include
+#include
+
+#include "../App/DisplayedFilesModel.h"
+#include "../App/RecentFilesModel.h"
+#include "../App/ExamplesModel.h"
+
+
+class QListView;
+class QGridLayout;
+class QScrollArea;
+
+namespace Gui
+{
+class Document;
+}
+
+namespace CleanStartGui
+{
+
+class CleanStartGuiExport CleanStartView: public Gui::MDIView
+{
+ Q_OBJECT
+
+ TYPESYSTEM_HEADER_WITH_OVERRIDE(); // NOLINT
+
+public:
+ CleanStartView(Gui::Document* pcDocument, QWidget* parent);
+
+ const char* getName() const override
+ {
+ return "CleanStartView";
+ }
+
+ void newEmptyFile() const;
+ void newPartDesignFile() const;
+ void openExistingFile() const;
+ void newAssemblyFile() const;
+ void newDraftFile() const;
+ void newArchFile() const;
+
+public:
+ enum class PostStartBehavior {
+ switchWorkbench,
+ doNotSwitchWorkbench
+ };
+
+protected:
+
+ void configureNewFileButtons(QGridLayout *layout) const;
+ static void configureFileCardWidget(QListView *fileCardWidget);
+ void configureRecentFilesListWidget(QListView *recentFilesListWidget);
+ void configureExamplesListWidget(QListView *examplesListWidget);
+
+ void postStart(PostStartBehavior behavior) const;
+
+ void fileCardSelected(const QModelIndex &index);
+
+private:
+ QScrollArea* _contents = nullptr;
+ CleanStart::RecentFilesModel _recentFilesModel;
+ CleanStart::ExamplesModel _examplesModel;
+
+
+}; // namespace CleanStartGui
+
+} // namespace CleanStartGui
+
+#endif // FREECAD_CLEANSTARTVIEW_H
diff --git a/src/Mod/CleanStart/Gui/Command.cpp b/src/Mod/CleanStart/Gui/Command.cpp
new file mode 100644
index 0000000000..1098157e56
--- /dev/null
+++ b/src/Mod/CleanStart/Gui/Command.cpp
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/****************************************************************************
+ * *
+ * Copyright (c) 2024 The FreeCAD Project Association AISBL *
+ * *
+ * This file is part of FreeCAD. *
+ * *
+ * FreeCAD is free software: you can redistribute it and/or modify it *
+ * under the terms of the GNU Lesser General Public License as *
+ * published by the Free Software Foundation, either version 2.1 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * FreeCAD 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 *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with FreeCAD. If not, see *
+ * . *
+ * *
+ ***************************************************************************/
+
+#include "PreCompiled.h"
+
+#include
+#include
+
+#include <3rdParty/GSL/include/gsl/pointers>
+
+#include "Workbench.h"
+
+
+using namespace std;
+
+DEF_STD_CMD(CmdCleanStart)
+
+CmdCleanStart::CmdCleanStart()
+ : Command("CleanStart_CleanStart")
+{
+ sAppModule = "CleanStart";
+ sGroup = QT_TR_NOOP("CleanStart");
+ sMenuText = QT_TR_NOOP("CleanStart");
+ sToolTipText = QT_TR_NOOP("Displays the CleanStart in an MDI view");
+ sWhatsThis = "CleanStart_CleanStart";
+ sStatusTip = sToolTipText;
+ sPixmap = "CleanStartWorkbench";
+}
+
+void CmdCleanStart::activated(int iMsg)
+{
+ Q_UNUSED(iMsg);
+ CleanStartGui::Workbench::loadCleanStart();
+}
+
+
+void CreateCleanStartCommands()
+{
+ Gui::CommandManager& rcCmdMgr = Gui::Application::Instance->commandManager();
+ auto newCommand = gsl::owner(new CmdCleanStart);
+ rcCmdMgr.addCommand(newCommand); // Transfer ownership
+}
diff --git a/src/Mod/CleanStart/Gui/FileCardDelegate.cpp b/src/Mod/CleanStart/Gui/FileCardDelegate.cpp
new file mode 100644
index 0000000000..b5af64b46c
--- /dev/null
+++ b/src/Mod/CleanStart/Gui/FileCardDelegate.cpp
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/****************************************************************************
+ * *
+ * Copyright (c) 2024 The FreeCAD Project Association AISBL *
+ * *
+ * This file is part of FreeCAD. *
+ * *
+ * FreeCAD is free software: you can redistribute it and/or modify it *
+ * under the terms of the GNU Lesser General Public License as *
+ * published by the Free Software Foundation, either version 2.1 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * FreeCAD 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 *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with FreeCAD. If not, see *
+ * . *
+ * *
+ ***************************************************************************/
+
+#include "PreCompiled.h"
+
+#ifndef _PreComp_
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#endif
+
+#include "FileCardDelegate.h"
+#include "../App/DisplayedFilesModel.h"
+#include "App/Application.h"
+#include <3rdParty/GSL/include/gsl/pointers>
+
+using namespace CleanStart;
+
+FileCardDelegate::FileCardDelegate(QObject *parent)
+ : QAbstractItemDelegate(parent) {
+ _parameterGroup = App::GetApplication().GetParameterGroupByPath(
+ "User parameter:BaseApp/Preferences/Mod/Start");
+}
+
+
+void FileCardDelegate::paint(QPainter *painter,
+ const QStyleOptionViewItem &option,
+ const QModelIndex &index) const {
+ auto thumbnailSize =
+ static_cast(_parameterGroup->GetInt("FileThumbnailIconsSize", 64)); // NOLINT
+ auto cardWidth = thumbnailSize;
+ auto baseName = index.data(static_cast(DisplayedFilesModelRoles::baseName)).toString();
+ auto size = index.data(static_cast(DisplayedFilesModelRoles::size)).toString();
+ auto image = index.data(static_cast(DisplayedFilesModelRoles::image)).toByteArray();
+ auto path = index.data(static_cast(DisplayedFilesModelRoles::path)).toString();
+ painter->save();
+ auto widget = gsl::owner(new QWidget());
+ auto layout = gsl::owner(new QVBoxLayout());
+ widget->setLayout(layout);
+ auto thumbnail = gsl::owner(new QLabel());
+ auto pixmap = gsl::owner(new QPixmap());
+ if (!image.isEmpty()) {
+ pixmap->loadFromData(image);
+ auto scaled = pixmap->scaled(QSize(thumbnailSize, thumbnailSize),
+ Qt::AspectRatioMode::KeepAspectRatio,
+ Qt::TransformationMode::SmoothTransformation);
+ thumbnail->setPixmap(scaled);
+ } else {
+ thumbnail->setPixmap(generateThumbnail(path));
+ }
+ thumbnail->setFixedSize(thumbnailSize, thumbnailSize);
+ thumbnail->setSizePolicy(QSizePolicy::Policy::Fixed, QSizePolicy::Policy::Fixed);
+ auto elided =
+ painter->fontMetrics().elidedText(baseName, Qt::TextElideMode::ElideRight, cardWidth);
+ auto name = gsl::owner(new QLabel(elided));
+ layout->addWidget(thumbnail);
+ layout->addWidget(name);
+ auto sizeLabel = gsl::owner(new QLabel(size));
+ layout->addWidget(sizeLabel);
+ layout->addStretch();
+ layout->setSpacing(0);
+ widget->resize(option.rect.size());
+ painter->translate(option.rect.topLeft());
+ widget->render(painter, QPoint(), QRegion(), QWidget::DrawChildren);
+ painter->restore();
+ delete pixmap;
+}
+
+
+QSize FileCardDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const {
+ Q_UNUSED(option)
+ Q_UNUSED(index)
+ auto thumbnailSize = _parameterGroup->GetInt("FileThumbnailIconsSize", 128); // NOLINT
+ auto cardSpacing = _parameterGroup->GetInt("FileCardSpacing", 20); // NOLINT
+ auto cardWidth = thumbnailSize + cardSpacing;
+
+ auto font = QGuiApplication::font();
+ auto qfm = QFontMetrics(font);
+ auto textHeight = 2 * qfm.lineSpacing();
+ auto cardHeight = thumbnailSize + textHeight + cardSpacing;
+
+ return {static_cast(cardWidth), static_cast(cardHeight)};
+}
+
+namespace {
+ QPixmap pixmapToSizedQImage(const QImage &pixmap, int size) {
+ return QPixmap::fromImage(pixmap).scaled(size, size,
+ Qt::AspectRatioMode::KeepAspectRatio,
+ Qt::TransformationMode::SmoothTransformation);
+ }
+}
+
+QPixmap FileCardDelegate::generateThumbnail(const QString &path) const {
+ auto thumbnailSize =
+ static_cast(_parameterGroup->GetInt("FileThumbnailIconsSize", 64)); // NOLINT
+ if (path.endsWith(QLatin1String(".fcstd"), Qt::CaseSensitivity::CaseInsensitive)) {
+ QImageReader reader(QLatin1String(":/icons/freecad-doc.svg"));;
+ reader.setScaledSize({thumbnailSize, thumbnailSize});
+ return QPixmap::fromImage(reader.read());
+ }
+ if (path.endsWith(QLatin1String(".fcmacro"), Qt::CaseSensitivity::CaseInsensitive)) {
+ QImageReader reader(QLatin1String(":/icons/MacroEditor.svg"));;
+ reader.setScaledSize({thumbnailSize, thumbnailSize});
+ return QPixmap::fromImage(reader.read());
+ }
+ if (!QImageReader::imageFormat(path).isEmpty()) {
+ // It is an image: it can be its own thumbnail
+ QImageReader reader(path);
+ auto image = reader.read();
+ return pixmapToSizedQImage(image, thumbnailSize);
+ }
+ QIcon icon = QFileIconProvider().icon(QFileInfo(path));
+ if (!icon.isNull()) {
+ QPixmap pixmap = icon.pixmap(thumbnailSize);
+ if (!pixmap.isNull()) {
+ return pixmap;
+ }
+ }
+ QPixmap pixmap = QPixmap(thumbnailSize, thumbnailSize);
+ pixmap.fill();
+ return pixmap;
+}
diff --git a/src/Mod/CleanStart/Gui/FileCardDelegate.h b/src/Mod/CleanStart/Gui/FileCardDelegate.h
new file mode 100644
index 0000000000..454563c130
--- /dev/null
+++ b/src/Mod/CleanStart/Gui/FileCardDelegate.h
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/****************************************************************************
+ * *
+ * Copyright (c) 2024 The FreeCAD Project Association AISBL *
+ * *
+ * This file is part of FreeCAD. *
+ * *
+ * FreeCAD is free software: you can redistribute it and/or modify it *
+ * under the terms of the GNU Lesser General Public License as *
+ * published by the Free Software Foundation, either version 2.1 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * FreeCAD 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 *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with FreeCAD. If not, see *
+ * . *
+ * *
+ ***************************************************************************/
+
+#ifndef FREECAD_CLEANSTART_FILECARDDELEGATE_H
+#define FREECAD_CLEANSTART_FILECARDDELEGATE_H
+
+#include
+#include
+
+#include
+
+class FileCardDelegate : public QAbstractItemDelegate {
+
+public:
+
+ explicit FileCardDelegate(QObject *parent = nullptr);
+
+ void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
+
+ QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
+
+protected:
+
+ QPixmap generateThumbnail(const QString &path) const;
+
+private:
+
+ Base::Reference _parameterGroup;
+};
+
+
+#endif //FREECAD_CLEANSTART_FILECARDDELEGATE_H
diff --git a/src/Mod/CleanStart/Gui/FileCardView.cpp b/src/Mod/CleanStart/Gui/FileCardView.cpp
new file mode 100644
index 0000000000..1ae1043468
--- /dev/null
+++ b/src/Mod/CleanStart/Gui/FileCardView.cpp
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/****************************************************************************
+ * *
+ * Copyright (c) 2024 The FreeCAD Project Association AISBL *
+ * *
+ * This file is part of FreeCAD. *
+ * *
+ * FreeCAD is free software: you can redistribute it and/or modify it *
+ * under the terms of the GNU Lesser General Public License as *
+ * published by the Free Software Foundation, either version 2.1 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * FreeCAD 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 *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with FreeCAD. If not, see *
+ * . *
+ * *
+ ***************************************************************************/
+
+#include "PreCompiled.h"
+#include "FileCardView.h"
+
+#include
+#include "../App/DisplayedFilesModel.h"
+
+namespace CleanStartGui
+{
+
+
+FileCardView::FileCardView(QWidget *parent) : QListView(parent)
+{
+ QSizePolicy sizePolicy(QSizePolicy::Policy::MinimumExpanding,
+ QSizePolicy::Policy::MinimumExpanding);
+ sizePolicy.setHeightForWidth(true);
+ setSizePolicy(sizePolicy);
+ setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff);
+ setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff);
+ setViewMode(QListView::ViewMode::IconMode);
+ setFlow(QListView::Flow::LeftToRight);
+ setResizeMode(QListView::ResizeMode::Adjust);
+ setUniformItemSizes(true);
+}
+
+int FileCardView::heightForWidth(int width) const
+{
+ auto model = this->model();
+ auto delegate = this->itemDelegate();
+ if (!model || !delegate) {
+ return 0;
+ }
+ int numCards = model->rowCount();
+ auto cardSize = delegate->sizeHint(QStyleOptionViewItem(), model->index(0, 0));
+ int cardsPerRow = static_cast(width / cardSize.width());
+ int numRows = static_cast (ceil(static_cast(numCards) / static_cast(cardsPerRow)));
+ int neededHeight = numRows * cardSize.height();
+ auto hGrp = App::GetApplication().GetParameterGroupByPath(
+ "User parameter:BaseApp/Preferences/Mod/Start");
+ int cardSpacing = static_cast(hGrp->GetInt("FileCardSpacing", 20)); // NOLINT
+ return neededHeight + cardSpacing*(numRows-1) + 2*cardSpacing;
+}
+
+QSize FileCardView::sizeHint() const
+{
+ auto hGrp = App::GetApplication().GetParameterGroupByPath(
+ "User parameter:BaseApp/Preferences/Mod/Start");
+ int cardSpacing = static_cast(hGrp->GetInt("FileCardSpacing", 20)); // NOLINT
+
+ auto model = this->model();
+ auto delegate = this->itemDelegate();
+ if (!model || !delegate) {
+ // The model and/or delegate have not been set yet, this was an early startup call
+ return {cardSpacing, cardSpacing};
+ }
+ int numCards = model->rowCount();
+ auto cardSize = delegate->sizeHint(QStyleOptionViewItem(), model->index(0, 0));
+ return {(cardSize.width()+cardSpacing) * numCards + cardSpacing, cardSize.height() + 2* cardSpacing};
+}
+
+
+} // namespace CleanStart
diff --git a/src/Mod/CleanStart/Gui/FileCardView.h b/src/Mod/CleanStart/Gui/FileCardView.h
new file mode 100644
index 0000000000..2e8994858a
--- /dev/null
+++ b/src/Mod/CleanStart/Gui/FileCardView.h
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/****************************************************************************
+ * *
+ * Copyright (c) 2024 The FreeCAD Project Association AISBL *
+ * *
+ * This file is part of FreeCAD. *
+ * *
+ * FreeCAD is free software: you can redistribute it and/or modify it *
+ * under the terms of the GNU Lesser General Public License as *
+ * published by the Free Software Foundation, either version 2.1 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * FreeCAD 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 *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with FreeCAD. If not, see *
+ * . *
+ * *
+ ***************************************************************************/
+
+#ifndef FREECAD_CLEANSTART_FILECARDVIEW_H
+#define FREECAD_CLEANSTART_FILECARDVIEW_H
+
+#include
+
+namespace CleanStartGui
+{
+
+class FileCardView : public QListView
+{
+ Q_OBJECT
+
+public:
+ explicit FileCardView(QWidget *parent = nullptr);
+
+ int heightForWidth(int width) const override;
+
+ QSize sizeHint() const override;
+
+};
+
+} // namespace CleanStart
+
+#endif // FREECAD_CLEANSTART_FILECARDVIEW_H
diff --git a/src/Mod/CleanStart/Gui/PreCompiled.cpp b/src/Mod/CleanStart/Gui/PreCompiled.cpp
new file mode 100644
index 0000000000..b17d7731bd
--- /dev/null
+++ b/src/Mod/CleanStart/Gui/PreCompiled.cpp
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/****************************************************************************
+ * *
+# Copyright (c) 2024 The FreeCAD Project Association AISBL *
+ * *
+ * This file is part of FreeCAD. *
+ * *
+ * FreeCAD is free software: you can redistribute it and/or modify it *
+ * under the terms of the GNU Lesser General Public License as *
+ * published by the Free Software Foundation, either version 2.1 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * FreeCAD 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 *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with FreeCAD. If not, see *
+ * . *
+ * *
+ ***************************************************************************/
+
+
+#include "PreCompiled.h"
diff --git a/src/Mod/CleanStart/Gui/PreCompiled.h b/src/Mod/CleanStart/Gui/PreCompiled.h
new file mode 100644
index 0000000000..3e6fc4cc7a
--- /dev/null
+++ b/src/Mod/CleanStart/Gui/PreCompiled.h
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/****************************************************************************
+ * *
+# Copyright (c) 2024 The FreeCAD Project Association AISBL *
+ * *
+ * This file is part of FreeCAD. *
+ * *
+ * FreeCAD is free software: you can redistribute it and/or modify it *
+ * under the terms of the GNU Lesser General Public License as *
+ * published by the Free Software Foundation, either version 2.1 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * FreeCAD 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 *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with FreeCAD. If not, see *
+ * . *
+ * *
+ ***************************************************************************/
+
+#ifndef CLEANSTARTGUI_PRECOMPILED_H
+#define CLEANSTARTGUI_PRECOMPILED_H
+
+#include
+
+#ifdef _MSC_VER
+#pragma warning(disable : 5208)
+#endif
+
+#ifdef _PreComp_
+
+// standard
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+// Qt
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#endif // _PreComp_
+#endif // CLEANSTARTGUI_PRECOMPILED_H
diff --git a/src/Mod/CleanStart/Gui/Resources/CleanStart.qrc b/src/Mod/CleanStart/Gui/Resources/CleanStart.qrc
new file mode 100644
index 0000000000..a6f27c69a4
--- /dev/null
+++ b/src/Mod/CleanStart/Gui/Resources/CleanStart.qrc
@@ -0,0 +1,5 @@
+
+
+ icons/CleanStartWorkbench.svg
+
+
diff --git a/src/Mod/CleanStart/Gui/Resources/icons/CleanStartWorkbench.svg b/src/Mod/CleanStart/Gui/Resources/icons/CleanStartWorkbench.svg
new file mode 100644
index 0000000000..92e7992912
--- /dev/null
+++ b/src/Mod/CleanStart/Gui/Resources/icons/CleanStartWorkbench.svg
@@ -0,0 +1,35 @@
+
+
+AAAsdWp1bWIAAAAeanVtZGMycGEAEQAQgAAAqgA4m3EDYzJwYQAAACxPanVtYgAAAEdqdW1kYzJtYQARABCAAACqADibcQN1cm46dXVpZDo0YjRiNjhiMC1jYWIyLTQ3NjgtYjgyMC0xMGZiMzRiMjIzNDAAAAABtWp1bWIAAAApanVtZGMyYXMAEQAQgAAAqgA4m3EDYzJwYS5hc3NlcnRpb25zAAAAANdqdW1iAAAAJmp1bWRjYm9yABEAEIAAAKoAOJtxA2MycGEuYWN0aW9ucwAAAACpY2JvcqFnYWN0aW9uc4GjZmFjdGlvbmtjMnBhLmVkaXRlZG1zb2Z0d2FyZUFnZW50bUFkb2JlIEZpcmVmbHlxZGlnaXRhbFNvdXJjZVR5cGV4U2h0dHA6Ly9jdi5pcHRjLm9yZy9uZXdzY29kZXMvZGlnaXRhbHNvdXJjZXR5cGUvY29tcG9zaXRlV2l0aFRyYWluZWRBbGdvcml0aG1pY01lZGlhAAAArWp1bWIAAAAoanVtZGNib3IAEQAQgAAAqgA4m3EDYzJwYS5oYXNoLmRhdGEAAAAAfWNib3KlamV4Y2x1c2lvbnOBomVzdGFydBkBo2ZsZW5ndGgZO0hkbmFtZW5qdW1iZiBtYW5pZmVzdGNhbGdmc2hhMjU2ZGhhc2hYIElELNIOhv56eICCdEZNoj2K8JEvFWRCJNlTbdf5JfACY3BhZEkAAAAAAAAAAAAAAAILanVtYgAAACRqdW1kYzJjbAARABCAAACqADibcQNjMnBhLmNsYWltAAAAAd9jYm9yqGhkYzp0aXRsZW9HZW5lcmF0ZWQgSW1hZ2VpZGM6Zm9ybWF0bWltYWdlL3N2Zyt4bWxqaW5zdGFuY2VJRHgseG1wOmlpZDo3ZWRhZGYxNi1jMzllLTQ5ZDUtYmNkZS00ZTBjNTc1YmQyZWJvY2xhaW1fZ2VuZXJhdG9yeDZBZG9iZV9JbGx1c3RyYXRvci8yOC4zIGFkb2JlX2MycGEvMC43LjYgYzJwYS1ycy8wLjI1LjJ0Y2xhaW1fZ2VuZXJhdG9yX2luZm+Bv2RuYW1lcUFkb2JlIElsbHVzdHJhdG9yZ3ZlcnNpb25kMjguM/9pc2lnbmF0dXJleBlzZWxmI2p1bWJmPWMycGEuc2lnbmF0dXJlamFzc2VydGlvbnOComN1cmx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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9lkBADaYjbNHBledrs8UvzcmIqg4q5h2I85dUCPn9QN1KzSHCFLAAEktdVGMxgi6L8yyKxQDXsxwIdEoCmi3IYtSe1SatqfzWegeTWgkpslSKyduJeUQbtePR6xJbavPMS1Li8dOlMInahvFdX8o/TSfg/exYQ/6aNF7/P/nscuzeu3ocpRXoNPlmaYfFxK1eLwOQL1Q2/KSuoQYOvq7mDGUA8LxdK0pN7IgRy70YUuXk5eXeVVb/aFXY7lwXeYuO+1inkxNNLY6Ndoe9iiu+XhXlhMNU2BiSYv40Ra8zOCuPfPKGTf3e/vLjW6HLo52X3G0DzD9uHaCZWYtZQ87IDR1+Kg=
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Mod/CleanStart/Gui/Workbench.cpp b/src/Mod/CleanStart/Gui/Workbench.cpp
new file mode 100644
index 0000000000..8623e6afd4
--- /dev/null
+++ b/src/Mod/CleanStart/Gui/Workbench.cpp
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/****************************************************************************
+ * *
+ * Copyright (c) 2024 The FreeCAD Project Association AISBL *
+ * *
+ * This file is part of FreeCAD. *
+ * *
+ * FreeCAD is free software: you can redistribute it and/or modify it *
+ * under the terms of the GNU Lesser General Public License as *
+ * published by the Free Software Foundation, either version 2.1 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * FreeCAD 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 *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with FreeCAD. If not, see *
+ * . *
+ * *
+ ***************************************************************************/
+
+#include "PreCompiled.h"
+#ifndef _PreComp_
+#include
+#include
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "Workbench.h"
+#include "CleanStartView.h"
+
+#include <3rdParty/GSL/include/gsl/pointers>
+
+using namespace CleanStartGui;
+
+TYPESYSTEM_SOURCE(CleanStartGui::Workbench, Gui::StdWorkbench) // NOLINT
+
+void CleanStartGui::Workbench::activated()
+{
+ loadCleanStart();
+}
+
+void CleanStartGui::Workbench::loadCleanStart()
+{
+ auto mw = Gui::getMainWindow();
+ auto doc = Gui::Application::Instance->activeDocument();
+ auto existingView = mw->findChild(QLatin1String("CleanStartView"));
+ if (!existingView) {
+ existingView = gsl::owner (new CleanStartView (doc, mw));
+ mw->addWindow(existingView); // Transfers ownership
+ }
+ Gui::getMainWindow()->setActiveWindow(existingView);
+ existingView->show();
+}
+
+Gui::MenuItem* CleanStartGui::Workbench::setupMenuBar() const
+{
+ return Gui::StdWorkbench::setupMenuBar();
+}
+
+Gui::ToolBarItem* CleanStartGui::Workbench::setupToolBars() const
+{
+ return Gui::StdWorkbench::setupToolBars();
+}
+
+Gui::ToolBarItem* CleanStartGui::Workbench::setupCommandBars() const
+{
+ return Gui::StdWorkbench::setupCommandBars();
+}
+
+Gui::DockWindowItems* CleanStartGui::Workbench::setupDockWindows() const
+{
+ Gui::DockWindowItems* root = Gui::StdWorkbench::setupDockWindows();
+ root->setVisibility(false); // hide all dock windows by default
+ root->setVisibility("Std_ComboView", true); // except of the combo view
+ root->setVisibility("Std_TaskView", true); // and the task view
+ return root;
+}
diff --git a/src/Mod/CleanStart/Gui/Workbench.h b/src/Mod/CleanStart/Gui/Workbench.h
new file mode 100644
index 0000000000..0e89d1a602
--- /dev/null
+++ b/src/Mod/CleanStart/Gui/Workbench.h
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/****************************************************************************
+ * *
+ * Copyright (c) 2024 The FreeCAD Project Association AISBL *
+ * *
+ * This file is part of FreeCAD. *
+ * *
+ * FreeCAD is free software: you can redistribute it and/or modify it *
+ * under the terms of the GNU Lesser General Public License as *
+ * published by the Free Software Foundation, either version 2.1 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * FreeCAD 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 *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with FreeCAD. If not, see *
+ * . *
+ * *
+ ***************************************************************************/
+
+
+#ifndef LAUNCHERGUI_WORKBENCH_H
+#define LAUNCHERGUI_WORKBENCH_H
+
+#include
+#include
+
+namespace Gui {
+class MDIView;
+}
+
+namespace CleanStartGui
+{
+
+class Workbench: public Gui::StdWorkbench
+{
+ TYPESYSTEM_HEADER_WITH_OVERRIDE(); // NOLINT
+
+public:
+ Workbench() = default;
+
+ /** Run some actions when the workbench gets activated. */
+ void activated() override;
+
+ static void loadCleanStart();
+
+protected:
+ /** Defines the standard menus. */
+ Gui::MenuItem* setupMenuBar() const override;
+ /** Defines the standard toolbars. */
+ Gui::ToolBarItem* setupToolBars() const override;
+ /** Defines the standard command bars. */
+ Gui::ToolBarItem* setupCommandBars() const override;
+ /** Returns a DockWindowItems structure of dock windows this workbench. */
+ Gui::DockWindowItems* setupDockWindows() const override;
+
+}; // class Workbench
+
+} // namespace CleanStartGui
+#endif // LAUNCHERGUI_WORKBENCH_H
diff --git a/src/Mod/CleanStart/Init.py b/src/Mod/CleanStart/Init.py
new file mode 100644
index 0000000000..c41703a22a
--- /dev/null
+++ b/src/Mod/CleanStart/Init.py
@@ -0,0 +1,29 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# /**************************************************************************
+# *
+# Copyright (c) 2024 The FreeCAD Project Association AISBL *
+# *
+# This file is part of FreeCAD. *
+# *
+# FreeCAD is free software: you can redistribute it and/or modify it *
+# under the terms of the GNU Lesser General Public License as *
+# published by the Free Software Foundation, either version 2.1 of the *
+# License, or (at your option) any later version. *
+# *
+# FreeCAD 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 *
+# Lesser General Public License for more details. *
+# *
+# You should have received a copy of the GNU Lesser General Public *
+# License along with FreeCAD. If not, see *
+# . *
+# *
+# **************************************************************************/
+
+# Get the Parameter Group of this module
+ParGrp = App.ParamGet("System parameter:Modules").GetGroup("CleanStart")
+
+# Set the needed information
+ParGrp.SetString("WorkBenchName", "CleanStart")
+ParGrp.SetString("WorkBenchModule", "CleanStart.py")
diff --git a/src/Mod/CleanStart/InitGui.py b/src/Mod/CleanStart/InitGui.py
new file mode 100644
index 0000000000..cf1e585af2
--- /dev/null
+++ b/src/Mod/CleanStart/InitGui.py
@@ -0,0 +1,51 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+# /**************************************************************************
+# *
+# Copyright (c) 2024 The FreeCAD Project Association AISBL *
+# *
+# This file is part of FreeCAD. *
+# *
+# FreeCAD is free software: you can redistribute it and/or modify it *
+# under the terms of the GNU Lesser General Public License as *
+# published by the Free Software Foundation, either version 2.1 of the *
+# License, or (at your option) any later version. *
+# *
+# FreeCAD 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 *
+# Lesser General Public License for more details. *
+# *
+# You should have received a copy of the GNU Lesser General Public *
+# License along with FreeCAD. If not, see *
+# . *
+# *
+# **************************************************************************/
+
+
+class CleanStartWorkbench(Workbench):
+ "CleanStartWorkbench workbench"
+
+ def __init__(self):
+ self.__class__.Icon = (
+ FreeCAD.getResourceDir() + "Mod/Start/Resources/icons/StartWorkbench.svg"
+ )
+ self.__class__.MenuText = "Start (new!)"
+ self.__class__.ToolTip = "Start workbench"
+
+ def Initialize(self):
+ import CleanStartGui
+
+ def Activated(self):
+ pass
+
+ def Deactivated(self):
+ pass
+
+ def ContextMenu(self, recipient):
+ pass
+
+ def GetClassName(self):
+ return "CleanStartGui::Workbench"
+
+
+Gui.addWorkbench(CleanStartWorkbench())
diff --git a/src/Mod/CleanStart/README.md b/src/Mod/CleanStart/README.md
new file mode 100644
index 0000000000..41e402c380
--- /dev/null
+++ b/src/Mod/CleanStart/README.md
@@ -0,0 +1,53 @@
+## CleanStart Workbench
+
+This Workbench is intended to eventually replace (and be renamed to) Start. Its main reason for existing is to
+eliminate FreeCAD's dependency on QtWebEngine by redesigning Start to eliminate the HTML component.
+
+The long-term plan for this workbench is to migrate from Qt Widgets to QtQuick/QML with a C++ backend providing access
+to things like FreeCAD's Recent Files list. This switch will happen sometime after we no longer have to support building
+on Ubuntu 20.04 LTS, which still uses Qt 5.12. The cMake integration of QML and C++ together in a single project is
+greatly improved in Qt 5.15 and later.
+
+In the meantime the workbench is written in C++ so that the models can be re-used later, and only the UI itself will
+have to change.
+
+### Structure
+
+The main UI file for the Start screen is in `Gui/CleanStartView.cpp` -- that class is a `QScrollArea` that gets embedded
+into a new FreeCAD `MDIView`. Inside the scroll area are three regions:
+
+1. **New File**. A set of `QPushButtons` with embedded `QLayouts` displaying an image and two lines of text. Currently
+laid out manually in a `QGridLayout`, but eventually it would be nice to dynamically calculate that layout to allow the
+buttons to exist on a single line when there is enough space for them.
+2. **Recent Files**. One of two "File Card" regions, this shows a list of recent files. It uses the
+Model-View-Controller architecture for flexibility and re-usability. In that architecture, the data being displayed is
+called the "model", and the actual mechanism for doing the display is called the "view". Qt further differentiates
+between the overall view and the display of the individual items in the view. The items are rendered by a "delegate"
+when they are too complex to be displayed by the simple view (e.g. if you want images and text in a particular layout).
+ * The "model" in this case is `RecentFilesModel`, a simple read-only interface to
+FreeCAD's preferences system, where it gets the list of recent files. That class is in`App/RecentFilesModel.*`. It is
+implemented using a set of User Roles, one for each piece of data about a file. Not all data is available for all files.
+For example, when given `const QModelIndex &index`, you can call `index.data(DisplayedFilesModelRoles::author)` to get
+the "author" metadata item of an `FCStd` file. See the `DisplayedFilesModelRoles` enumeration for possible values. These
+roles are also exposed to QML via their names.
+ * The View is a class derived from `QListView`, `Gui/FileCardView.*`, whose only function beyond the standard
+`QListView` is to implement the "height for width" functionality, so the widget can properly resize based on the number
+of file cards and the screen width, laying them out in a grid.
+ * The file cards are rendered using the `FileCardDelegate` class in `Gui/FileCardDelegate.*`. That class uses
+a simple `QVBoxLayout` to paint the icon, filename, and file size.
+3. **Examples**. Another "File Card" widget, using the same classes as Recent Files, but with a different model. In this
+case the model is `ExamplesModel` in `App/ExamplesModel.*`. It fetches a read-only list of files from the FreeCAD
+`resources/examples` directory and displays them.
+
+### UI Design
+
+This Workbench does the minimum amount of design customization, preferring to allow Stylesheet authors control over the
+display via the normal QSS mechanisms in Qt. There are three FreeCAD Parameters that control the spacing between the
+widgets and the size of the icons, all in the `BaseApp/Preferences/Mod/Start/` preferences group:
+* `FileCardSpacing` (default: 20). The space between and around the individual File Cards.
+* `FileThumbnailIconsSize` (default: 128). The size of the file thumbnail on the File Cards.
+* `NewFileIconSize` (default: 48). The size of the icons on each of the "new file" buttons.
+
+At present none of these are directly exposed to the user, and the new Start workbench does not have a preferences
+panel. The parameters are intended to be used by Preference Pack authors to assist in customization of FreeCAD themes.
+It is likely that this will be expanded once feedback from theme designers is received.
\ No newline at end of file