Interface mockup 13

This commit is contained in:
Dion Moult
2026-05-09 07:11:58 +10:00
parent d475553cd0
commit 3e066443be
12 changed files with 399 additions and 394 deletions
+4 -4
View File
@@ -90,7 +90,7 @@ void AddModelDialog::setupUi() {
row->setContentsMargins(0, 0, 0, 0);
row->setSpacing(components::style::metrics::padding);
auto* add_ifc = components::buttons::makeButton("Add IFC File", ":/icons/cube.svg", choices, QSize(90, 68));
auto* add_ifc = components::buttons::makeButton("Add IFC File", ":/icons/cube.svg", choices);
connect(add_ifc, &QToolButton::clicked, this, [this]() {
selected_mode_ = SourceMode::IfcFile;
accept();
@@ -100,7 +100,7 @@ void AddModelDialog::setupUi() {
"Add IFC files and load both geometry and data.",
default_description));
auto* add_database = components::buttons::makeButton("Add IFC\nDatabase", ":/icons/database.svg", choices, QSize(90, 68));
auto* add_database = components::buttons::makeButton("Add IFC\nDatabase", ":/icons/database.svg", choices);
connect(add_database, &QToolButton::clicked, this, [this]() {
selected_mode_ = SourceMode::IfcDatabase;
accept();
@@ -110,7 +110,7 @@ void AddModelDialog::setupUi() {
"Add IFC RDB databases for optimised performance",
default_description));
auto* add_geometry = components::buttons::makeButton("Add Geometry", ":/icons/cube-bandage.svg", choices, QSize(90, 68));
auto* add_geometry = components::buttons::makeButton("Add Geometry", ":/icons/cube-bandage.svg", choices);
connect(add_geometry, &QToolButton::clicked, this, [this]() {
selected_mode_ = SourceMode::GeometryOnly;
accept();
@@ -120,7 +120,7 @@ void AddModelDialog::setupUi() {
"Add pure geometry for fast visualisation",
default_description));
auto* convert_database = components::buttons::makeButton("Convert IFC File\nto Database", ":/icons/database-restore.svg", choices, QSize(90, 68));
auto* convert_database = components::buttons::makeButton("Convert IFC File\nto Database", ":/icons/database-restore.svg", choices);
connect(convert_database, &QToolButton::clicked, this, [description]() {
description->setText("IFC-to-database conversion is coming soon.");
});
+207
View File
@@ -0,0 +1,207 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell 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 *
* Lesser GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
#include "Controller.h"
#include "../../ElementRegistry.h"
#include "../../SessionState.h"
#include "../models/Controller.h"
#include "../viewport/Controller.h"
#include "../../../ifcviewer/Federation.h"
#include "../../../ifcviewer/SceneLoader.h"
#include "../../../ifcviewer/ViewportWindow.h"
#include <QFileDialog>
#include <QFileInfo>
#include <QMessageBox>
namespace ifcinterface::panels::project {
ProjectController::ProjectController(QWidget* host,
Federation* federation,
ifcinterface::SessionState* session_state,
ifcinterface::ElementRegistry* element_registry,
ViewportWindow* viewport,
ifcinterface::panels::models::ModelsPanelController* models_controller,
ifcinterface::panels::viewport::ViewportController* viewport_controller,
QObject* parent)
: QObject(parent)
, host_(host)
, federation_(federation)
, session_state_(session_state)
, element_registry_(element_registry)
, viewport_(viewport)
, models_controller_(models_controller)
, viewport_controller_(viewport_controller)
{
}
bool ProjectController::newProject() {
SceneLoader* loader = session_state_->loader();
if (loader && loader->isLoading()) {
QMessageBox::information(
host_, "New Project",
"Wait until the current model load finishes before creating a new project.");
return false;
}
if (!confirmDiscardIfDirty()) return false;
clearScene();
federation_->clear();
viewport_controller_->applyFederatedFalseOrigin();
session_state_->setStatusMessage("Project", "Untitled");
session_state_->notifyProjectReset();
return true;
}
bool ProjectController::openProject() {
QFileDialog file_dialog(host_, "Open Project");
file_dialog.setFileMode(QFileDialog::ExistingFile);
file_dialog.setNameFilter("IFC Federation (*.ifcfed);;All Files (*)");
file_dialog.setOption(QFileDialog::DontUseNativeDialog, true);
if (file_dialog.exec() != QDialog::Accepted) return false;
const QString path = file_dialog.selectedFiles().value(0);
if (path.isEmpty()) return false;
return openProject(path);
}
bool ProjectController::openProject(const QString& path) {
SceneLoader* loader = session_state_->loader();
if (loader && loader->isLoading()) {
QMessageBox::information(
host_, "Open Project",
"Wait until the current model load finishes before opening another project.");
return false;
}
if (!confirmDiscardIfDirty()) return false;
QStringList warnings;
QString err;
if (!federation_->load(path, &warnings, &err)) {
QMessageBox::warning(host_, "Open Project",
QString("Could not open project:\n%1").arg(err));
return false;
}
clearScene();
QStringList paths;
QStringList fed_ids;
for (const auto& model : federation_->models()) {
if (model.source_kind != "local") continue;
if (!QFileInfo::exists(model.source_path)) {
warnings << QString("Source not found, kept in project: %1").arg(model.source_path);
continue;
}
paths << model.source_path;
fed_ids << model.id;
}
models_controller_->loadModels(paths, fed_ids);
if (!warnings.isEmpty()) {
QMessageBox::warning(host_, "Open Project",
"Project opened with warnings:\n\n" + warnings.join("\n"));
}
federation_->markClean();
viewport_controller_->applyFederatedFalseOrigin();
if (federation_->hasHomeView()) {
const auto& hv = federation_->homeView();
viewport_->setCamera(
hv.target.x(), hv.target.y(), hv.target.z(), hv.distance, hv.yaw, hv.pitch);
}
session_state_->setStatusMessage("Project", QFileInfo(path).fileName());
session_state_->notifyProjectOpened(path);
return true;
}
bool ProjectController::saveProject() {
if (federation_->filePath().isEmpty()) return saveProjectAs();
QString err;
if (!federation_->save(federation_->filePath(), &err)) {
QMessageBox::warning(host_, "Save Project",
QString("Could not save project:\n%1").arg(err));
return false;
}
session_state_->setStatusMessage("Project", QFileInfo(federation_->filePath()).fileName());
session_state_->notifyProjectSaved(federation_->filePath());
return true;
}
bool ProjectController::saveProjectAs() {
QString suggested = federation_->filePath();
if (suggested.isEmpty()) suggested = "project.ifcfed";
QFileDialog file_dialog(host_, "Save Project As", suggested);
file_dialog.setAcceptMode(QFileDialog::AcceptSave);
file_dialog.setFileMode(QFileDialog::AnyFile);
file_dialog.setNameFilter("IFC Federation (*.ifcfed);;All Files (*)");
file_dialog.setOption(QFileDialog::DontUseNativeDialog, true);
if (file_dialog.exec() != QDialog::Accepted) return false;
QString path = file_dialog.selectedFiles().value(0);
if (path.isEmpty()) return false;
if (!path.endsWith(".ifcfed", Qt::CaseInsensitive)) path += ".ifcfed";
return saveProjectAs(path);
}
bool ProjectController::saveProjectAs(const QString& path) {
QString err;
if (!federation_->save(path, &err)) {
QMessageBox::warning(host_, "Save Project",
QString("Could not save project:\n%1").arg(err));
return false;
}
session_state_->setStatusMessage("Project", QFileInfo(path).fileName());
session_state_->notifyProjectSaved(path);
return true;
}
void ProjectController::clearScene() {
viewport_->setSelectedObjectId(0);
session_state_->setSelectedObjectId(0);
session_state_->notifySelectionChanged();
const auto model_ids = session_state_->modelIds();
for (uint32_t mid : model_ids) {
viewport_->removeModel(mid);
session_state_->loader()->removeModel(mid);
}
session_state_->clearModelMappings();
element_registry_->clear();
session_state_->notifyModelsChanged();
}
bool ProjectController::confirmDiscardIfDirty() {
if (!federation_->isDirty()) return true;
const auto result = QMessageBox::question(
host_, "Unsaved Project",
"The current project has unsaved changes. Save before continuing?",
QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel,
QMessageBox::Save);
if (result == QMessageBox::Cancel) return false;
if (result == QMessageBox::Save) return saveProject();
return true;
}
} // namespace ifcinterface::panels::project
+71
View File
@@ -0,0 +1,71 @@
// This file was generated with the assistance of an AI coding tool.
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell 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 *
* Lesser GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
#ifndef IFCINTERFACE_PANELS_PROJECT_CONTROLLER_H
#define IFCINTERFACE_PANELS_PROJECT_CONTROLLER_H
#include <QObject>
class QWidget;
class Federation;
class ViewportWindow;
namespace ifcinterface { class ElementRegistry; }
namespace ifcinterface { class SessionState; }
namespace ifcinterface::panels::models { class ModelsPanelController; }
namespace ifcinterface::panels::viewport { class ViewportController; }
namespace ifcinterface::panels::project {
class ProjectController : public QObject {
Q_OBJECT
public:
explicit ProjectController(QWidget* host,
Federation* federation,
ifcinterface::SessionState* session_state,
ifcinterface::ElementRegistry* element_registry,
ViewportWindow* viewport,
ifcinterface::panels::models::ModelsPanelController* models_controller,
ifcinterface::panels::viewport::ViewportController* viewport_controller,
QObject* parent = nullptr);
bool newProject();
bool openProject();
bool saveProject();
bool saveProjectAs();
private:
bool openProject(const QString& path);
bool saveProjectAs(const QString& path);
void clearScene();
bool confirmDiscardIfDirty();
QWidget* host_ = nullptr;
Federation* federation_ = nullptr;
ifcinterface::SessionState* session_state_ = nullptr;
ifcinterface::ElementRegistry* element_registry_ = nullptr;
ViewportWindow* viewport_ = nullptr;
ifcinterface::panels::models::ModelsPanelController* models_controller_ = nullptr;
ifcinterface::panels::viewport::ViewportController* viewport_controller_ = nullptr;
};
} // namespace ifcinterface::panels::project
#endif
@@ -148,6 +148,31 @@ void ViewportController::applyFederatedFalseOrigin() {
composeFederatedFalseOrigin(federation->federatedFalseOrigin(), federation->config()));
}
void ViewportController::setHomeView() {
auto camera = viewport_->cameraState();
Federation::HomeView home_view;
home_view.target = camera.target;
home_view.distance = camera.distance;
home_view.yaw = camera.yaw;
home_view.pitch = camera.pitch;
session_state_->federation()->setHomeView(home_view);
session_state_->setStatusMessage("Camera", "Home view updated");
}
void ViewportController::goHomeView() {
Federation* federation = session_state_->federation();
if (!federation->hasHomeView()) {
session_state_->setStatusMessage("Camera", "No home view set for this project");
return;
}
const auto& home_view = federation->homeView();
viewport_->setCamera(
home_view.target.x(), home_view.target.y(), home_view.target.z(),
home_view.distance, home_view.yaw, home_view.pitch);
session_state_->setStatusMessage("Camera", "Home view restored");
}
void ViewportController::maybeGuessFederatedFalseOrigin(uint32_t mid) {
Federation* federation = session_state_->federation();
SceneLoader* loader = session_state_->loader();
@@ -37,6 +37,8 @@ public:
QObject* parent = nullptr);
void applyFederatedFalseOrigin();
void setHomeView();
void goHomeView();
private:
void applyCoordinateOperation(uint32_t mid);