mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-31 08:56:34 +00:00
Interface mockup 13
This commit is contained in:
@@ -63,6 +63,8 @@ set(INTERFACE_FILES
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/panels/properties/Widget.h
|
${CMAKE_CURRENT_SOURCE_DIR}/panels/properties/Widget.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/panels/properties/View.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/panels/properties/View.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/panels/properties/View.h
|
${CMAKE_CURRENT_SOURCE_DIR}/panels/properties/View.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/panels/project/Controller.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/panels/project/Controller.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/panels/settings/Dialog.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/panels/settings/Dialog.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/panels/settings/Dialog.h
|
${CMAKE_CURRENT_SOURCE_DIR}/panels/settings/Dialog.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/panels/spatial_hierarchy/Types.h
|
${CMAKE_CURRENT_SOURCE_DIR}/panels/spatial_hierarchy/Types.h
|
||||||
|
|||||||
+77
-359
@@ -30,11 +30,11 @@
|
|||||||
#include "components/Panel.h"
|
#include "components/Panel.h"
|
||||||
#include "components/Style.h"
|
#include "components/Style.h"
|
||||||
#include "components/Tabs.h"
|
#include "components/Tabs.h"
|
||||||
#include "panels/add_model/Dialog.h"
|
|
||||||
#include "panels/models/Controller.h"
|
#include "panels/models/Controller.h"
|
||||||
#include "panels/todo/Widget.h"
|
#include "panels/todo/Widget.h"
|
||||||
#include "panels/models/View.h"
|
#include "panels/models/View.h"
|
||||||
#include "panels/models/Widget.h"
|
#include "panels/models/Widget.h"
|
||||||
|
#include "panels/project/Controller.h"
|
||||||
#include "panels/properties/View.h"
|
#include "panels/properties/View.h"
|
||||||
#include "panels/properties/Widget.h"
|
#include "panels/properties/Widget.h"
|
||||||
#include "panels/settings/Dialog.h"
|
#include "panels/settings/Dialog.h"
|
||||||
@@ -44,17 +44,14 @@
|
|||||||
#include "panels/viewport/Widget.h"
|
#include "panels/viewport/Widget.h"
|
||||||
|
|
||||||
#include <QDockWidget>
|
#include <QDockWidget>
|
||||||
#include <QFileDialog>
|
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QListView>
|
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QSignalBlocker>
|
#include <QSignalBlocker>
|
||||||
#include <QStackedWidget>
|
#include <QStackedWidget>
|
||||||
#include <QStatusBar>
|
#include <QStatusBar>
|
||||||
#include <QTreeView>
|
|
||||||
#include <QToolButton>
|
#include <QToolButton>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
@@ -72,6 +69,14 @@ MainWindow::MainWindow(QWidget* parent)
|
|||||||
setWindowModified(dirty);
|
setWindowModified(dirty);
|
||||||
updateWindowTitle();
|
updateWindowTitle();
|
||||||
});
|
});
|
||||||
|
connect(session_state_, &ifcinterface::SessionState::modelsChanged,
|
||||||
|
this, &MainWindow::updateWindowTitle);
|
||||||
|
connect(session_state_, &ifcinterface::SessionState::projectOpened,
|
||||||
|
this, [this](const QString&) { updateWindowTitle(); });
|
||||||
|
connect(session_state_, &ifcinterface::SessionState::projectSaved,
|
||||||
|
this, [this](const QString&) { updateWindowTitle(); });
|
||||||
|
connect(session_state_, &ifcinterface::SessionState::projectReset,
|
||||||
|
this, &MainWindow::updateWindowTitle);
|
||||||
setupChrome();
|
setupChrome();
|
||||||
setupViewport();
|
setupViewport();
|
||||||
setupPanels();
|
setupPanels();
|
||||||
@@ -89,16 +94,8 @@ void MainWindow::setupChrome() {
|
|||||||
updateWindowTitle();
|
updateWindowTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
QToolButton* MainWindow::makeRibbonAction(const QString& text, const QString& icon_path) {
|
|
||||||
return components::buttons::makeButton(text, icon_path, this, QSize(90, 54));
|
|
||||||
}
|
|
||||||
|
|
||||||
QWidget* MainWindow::makeRibbonGroup(const QString& title, const QList<QToolButton*>& buttons) {
|
|
||||||
return components::buttons::makeButtonGroup(title, buttons, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
QToolButton* MainWindow::makePanelToggle(const QString& text, QDockWidget* dock) {
|
QToolButton* MainWindow::makePanelToggle(const QString& text, QDockWidget* dock) {
|
||||||
auto* button = makeRibbonAction(text, ":/icons/sidebar-expand.svg");
|
auto* button = components::buttons::makeButton(text, ":/icons/sidebar-expand.svg", this);
|
||||||
button->setCheckable(true);
|
button->setCheckable(true);
|
||||||
button->setChecked(dock->isVisible());
|
button->setChecked(dock->isVisible());
|
||||||
connect(button, &QToolButton::toggled, dock, [dock](bool checked) {
|
connect(button, &QToolButton::toggled, dock, [dock](bool checked) {
|
||||||
@@ -119,39 +116,49 @@ QWidget* MainWindow::buildHomeRibbonPage() {
|
|||||||
row->setContentsMargins(6, 4, 6, 4);
|
row->setContentsMargins(6, 4, 6, 4);
|
||||||
row->setSpacing(0);
|
row->setSpacing(0);
|
||||||
|
|
||||||
auto* new_project = makeRibbonAction("New Project", ":/icons/plus-square.svg");
|
auto* new_project = components::buttons::makeButton("New Project", ":/icons/plus-square.svg", this);
|
||||||
connect(new_project, &QToolButton::clicked, this, &MainWindow::onNewProject);
|
connect(new_project, &QToolButton::clicked, this, [this]() {
|
||||||
auto* open_project = makeRibbonAction("Open Project", ":/icons/download-square.svg");
|
project_controller_->newProject();
|
||||||
connect(open_project, &QToolButton::clicked, this, &MainWindow::onOpenProject);
|
});
|
||||||
auto* open_cloud = makeRibbonAction("Open Cloud", ":/icons/cloud-square.svg");
|
auto* open_project = components::buttons::makeButton("Open Project", ":/icons/download-square.svg", this);
|
||||||
|
connect(open_project, &QToolButton::clicked, this, [this]() {
|
||||||
|
project_controller_->openProject();
|
||||||
|
});
|
||||||
|
auto* open_cloud = components::buttons::makeButton("Open Cloud", ":/icons/cloud-square.svg", this);
|
||||||
connect(open_cloud, &QToolButton::clicked, this, [this]() {
|
connect(open_cloud, &QToolButton::clicked, this, [this]() {
|
||||||
session_state_->setStatusMessage("Project", "Open Cloud Project coming soon");
|
session_state_->setStatusMessage("Project", "Open Cloud Project coming soon");
|
||||||
});
|
});
|
||||||
auto* open_recent = makeRibbonAction("Open Recent", ":/icons/clock-rotate-right.svg");
|
auto* open_recent = components::buttons::makeButton("Open Recent", ":/icons/clock-rotate-right.svg", this);
|
||||||
connect(open_recent, &QToolButton::clicked, this, [this]() {
|
connect(open_recent, &QToolButton::clicked, this, [this]() {
|
||||||
session_state_->setStatusMessage("Project", "Open Recent coming soon");
|
session_state_->setStatusMessage("Project", "Open Recent coming soon");
|
||||||
});
|
});
|
||||||
auto* save_project = makeRibbonAction("Save Project", ":/icons/floppy-disk.svg");
|
auto* save_project = components::buttons::makeButton("Save Project", ":/icons/floppy-disk.svg", this);
|
||||||
connect(save_project, &QToolButton::clicked, this, &MainWindow::onSaveProject);
|
connect(save_project, &QToolButton::clicked, this, [this]() {
|
||||||
auto* save_project_as = makeRibbonAction("Save As", ":/icons/floppy-disk-arrow-in.svg");
|
project_controller_->saveProject();
|
||||||
connect(save_project_as, &QToolButton::clicked, this, &MainWindow::onSaveProjectAs);
|
});
|
||||||
|
auto* save_project_as = components::buttons::makeButton("Save As", ":/icons/floppy-disk-arrow-in.svg", this);
|
||||||
|
connect(save_project_as, &QToolButton::clicked, this, [this]() {
|
||||||
|
project_controller_->saveProjectAs();
|
||||||
|
});
|
||||||
|
|
||||||
auto* add_model = makeRibbonAction("Add Model", ":/icons/cube.svg");
|
auto* add_model = components::buttons::makeButton("Add Model", ":/icons/cube.svg", this);
|
||||||
connect(add_model, &QToolButton::clicked, this, &MainWindow::onAddFiles);
|
connect(add_model, &QToolButton::clicked, this, [this]() {
|
||||||
auto* sync_models = makeRibbonAction("Sync Models", ":/icons/refresh-double.svg");
|
models_controller_->addFiles();
|
||||||
|
});
|
||||||
|
auto* sync_models = components::buttons::makeButton("Sync Models", ":/icons/refresh-double.svg", this);
|
||||||
connect(sync_models, &QToolButton::clicked, this, [this]() {
|
connect(sync_models, &QToolButton::clicked, this, [this]() {
|
||||||
session_state_->setStatusMessage("Models", "Sync models coming soon");
|
session_state_->setStatusMessage("Models", "Sync models coming soon");
|
||||||
});
|
});
|
||||||
|
|
||||||
auto* settings_button = makeRibbonAction("Settings", ":/icons/settings.svg");
|
auto* settings_button = components::buttons::makeButton("Settings", ":/icons/settings.svg", this);
|
||||||
connect(settings_button, &QToolButton::clicked, this, [this]() {
|
connect(settings_button, &QToolButton::clicked, this, [this]() {
|
||||||
panels::settings::SettingsDialog dialog(this);
|
panels::settings::SettingsDialog dialog(this);
|
||||||
dialog.exec();
|
dialog.exec();
|
||||||
});
|
});
|
||||||
|
|
||||||
row->addWidget(makeRibbonGroup("PROJECT", {new_project, open_project, open_cloud, open_recent, save_project, save_project_as}));
|
row->addWidget(components::buttons::makeButtonGroup("PROJECT", {new_project, open_project, open_cloud, open_recent, save_project, save_project_as}, this));
|
||||||
row->addWidget(makeRibbonGroup("MODELS", {add_model, sync_models}));
|
row->addWidget(components::buttons::makeButtonGroup("MODELS", {add_model, sync_models}, this));
|
||||||
row->addWidget(makeRibbonGroup("SETTINGS", {settings_button}));
|
row->addWidget(components::buttons::makeButtonGroup("SETTINGS", {settings_button}, this));
|
||||||
row->addStretch(1);
|
row->addStretch(1);
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
@@ -163,36 +170,40 @@ QWidget* MainWindow::buildNavigateRibbonPage() {
|
|||||||
row->setContentsMargins(2, 4, 2, 4);
|
row->setContentsMargins(2, 4, 2, 4);
|
||||||
row->setSpacing(0);
|
row->setSpacing(0);
|
||||||
|
|
||||||
auto* set_home = makeRibbonAction("Set Home", ":/icons/home.svg");
|
auto* set_home = components::buttons::makeButton("Set Home", ":/icons/home.svg", this);
|
||||||
connect(set_home, &QToolButton::clicked, this, &MainWindow::onSetHomeView);
|
connect(set_home, &QToolButton::clicked, this, [this]() {
|
||||||
auto* go_home = makeRibbonAction("Go Home", ":/icons/home-alt.svg");
|
viewport_controller_->setHomeView();
|
||||||
connect(go_home, &QToolButton::clicked, this, &MainWindow::onGoHomeView);
|
});
|
||||||
auto* view_all = makeRibbonAction("View All", ":/icons/cube-scan.svg");
|
auto* go_home = components::buttons::makeButton("Go Home", ":/icons/home-alt.svg", this);
|
||||||
|
connect(go_home, &QToolButton::clicked, this, [this]() {
|
||||||
|
viewport_controller_->goHomeView();
|
||||||
|
});
|
||||||
|
auto* view_all = components::buttons::makeButton("View All", ":/icons/cube-scan.svg", this);
|
||||||
connect(view_all, &QToolButton::clicked, this, [this]() {
|
connect(view_all, &QToolButton::clicked, this, [this]() {
|
||||||
if (viewport_widget_) viewport_widget_->viewport()->viewAll();
|
if (viewport_widget_) viewport_widget_->viewport()->viewAll();
|
||||||
});
|
});
|
||||||
auto* view_selected = makeRibbonAction("View Selected", ":/icons/cube-scan-solid.svg");
|
auto* view_selected = components::buttons::makeButton("View Selected", ":/icons/cube-scan-solid.svg", this);
|
||||||
connect(view_selected, &QToolButton::clicked, this, [this]() {
|
connect(view_selected, &QToolButton::clicked, this, [this]() {
|
||||||
if (viewport_widget_) viewport_widget_->viewport()->focusOnSelectedObject();
|
if (viewport_widget_) viewport_widget_->viewport()->focusOnSelectedObject();
|
||||||
});
|
});
|
||||||
|
|
||||||
auto* plan_view = makeRibbonAction("Plan", ":/icons/planimetry.svg");
|
auto* plan_view = components::buttons::makeButton("Plan", ":/icons/planimetry.svg", this);
|
||||||
connect(plan_view, &QToolButton::clicked, this, [this]() {
|
connect(plan_view, &QToolButton::clicked, this, [this]() {
|
||||||
if (viewport_widget_) viewport_widget_->viewport()->setStandardView(90.0f, 90.0f);
|
if (viewport_widget_) viewport_widget_->viewport()->setStandardView(90.0f, 90.0f);
|
||||||
});
|
});
|
||||||
auto* front_view = makeRibbonAction("Front", ":/icons/city.svg");
|
auto* front_view = components::buttons::makeButton("Front", ":/icons/city.svg", this);
|
||||||
connect(front_view, &QToolButton::clicked, this, [this]() {
|
connect(front_view, &QToolButton::clicked, this, [this]() {
|
||||||
if (viewport_widget_) viewport_widget_->viewport()->setStandardView(0.0f, 0.0f);
|
if (viewport_widget_) viewport_widget_->viewport()->setStandardView(0.0f, 0.0f);
|
||||||
});
|
});
|
||||||
auto* side_view = makeRibbonAction("Side", ":/icons/building.svg");
|
auto* side_view = components::buttons::makeButton("Side", ":/icons/building.svg", this);
|
||||||
connect(side_view, &QToolButton::clicked, this, [this]() {
|
connect(side_view, &QToolButton::clicked, this, [this]() {
|
||||||
if (viewport_widget_) viewport_widget_->viewport()->setStandardView(90.0f, 0.0f);
|
if (viewport_widget_) viewport_widget_->viewport()->setStandardView(90.0f, 0.0f);
|
||||||
});
|
});
|
||||||
auto* align_object = makeRibbonAction("Align Object", ":/icons/cellar.svg");
|
auto* align_object = components::buttons::makeButton("Align Object", ":/icons/cellar.svg", this);
|
||||||
connect(align_object, &QToolButton::clicked, this, [this]() {
|
connect(align_object, &QToolButton::clicked, this, [this]() {
|
||||||
session_state_->setStatusMessage("Orientation", "Align to object coming soon");
|
session_state_->setStatusMessage("Orientation", "Align to object coming soon");
|
||||||
});
|
});
|
||||||
auto* projection_button = makeRibbonAction("Perspective", ":/icons/perspective-view.svg");
|
auto* projection_button = components::buttons::makeButton("Perspective", ":/icons/perspective-view.svg", this);
|
||||||
connect(projection_button, &QToolButton::clicked, this, [this, projection_button]() {
|
connect(projection_button, &QToolButton::clicked, this, [this, projection_button]() {
|
||||||
if (!viewport_widget_) return;
|
if (!viewport_widget_) return;
|
||||||
viewport_widget_->viewport()->toggleProjection();
|
viewport_widget_->viewport()->toggleProjection();
|
||||||
@@ -200,18 +211,18 @@ QWidget* MainWindow::buildNavigateRibbonPage() {
|
|||||||
viewport_widget_->viewport()->projectionOrtho() ? "Ortho" : "Perspective");
|
viewport_widget_->viewport()->projectionOrtho() ? "Ortho" : "Perspective");
|
||||||
});
|
});
|
||||||
|
|
||||||
auto* orbit_mode = makeRibbonAction("Orbit", ":/icons/rotate-camera-right.svg");
|
auto* orbit_mode = components::buttons::makeButton("Orbit", ":/icons/rotate-camera-right.svg", this);
|
||||||
connect(orbit_mode, &QToolButton::clicked, this, [this]() {
|
connect(orbit_mode, &QToolButton::clicked, this, [this]() {
|
||||||
session_state_->setStatusMessage("Mode", "Orbit mode active");
|
session_state_->setStatusMessage("Mode", "Orbit mode active");
|
||||||
});
|
});
|
||||||
auto* fly_mode = makeRibbonAction("Fly", ":/icons/drone.svg");
|
auto* fly_mode = components::buttons::makeButton("Fly", ":/icons/drone.svg", this);
|
||||||
connect(fly_mode, &QToolButton::clicked, this, [this]() {
|
connect(fly_mode, &QToolButton::clicked, this, [this]() {
|
||||||
session_state_->setStatusMessage("Mode", "Fly mode coming soon");
|
session_state_->setStatusMessage("Mode", "Fly mode coming soon");
|
||||||
});
|
});
|
||||||
|
|
||||||
row->addWidget(makeRibbonGroup("CAMERA", {set_home, go_home, view_all, view_selected}));
|
row->addWidget(components::buttons::makeButtonGroup("CAMERA", {set_home, go_home, view_all, view_selected}, this));
|
||||||
row->addWidget(makeRibbonGroup("ORIENTATION", {plan_view, front_view, side_view, align_object, projection_button}));
|
row->addWidget(components::buttons::makeButtonGroup("ORIENTATION", {plan_view, front_view, side_view, align_object, projection_button}, this));
|
||||||
row->addWidget(makeRibbonGroup("MODE", {orbit_mode, fly_mode}));
|
row->addWidget(components::buttons::makeButtonGroup("MODE", {orbit_mode, fly_mode}, this));
|
||||||
row->addStretch(1);
|
row->addStretch(1);
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
@@ -223,38 +234,38 @@ QWidget* MainWindow::buildInspectRibbonPage() {
|
|||||||
row->setContentsMargins(2, 4, 2, 4);
|
row->setContentsMargins(2, 4, 2, 4);
|
||||||
row->setSpacing(0);
|
row->setSpacing(0);
|
||||||
|
|
||||||
auto* hide_selected = makeRibbonAction("Hide", ":/icons/eye-closed.svg");
|
auto* hide_selected = components::buttons::makeButton("Hide", ":/icons/eye-closed.svg", this);
|
||||||
connect(hide_selected, &QToolButton::clicked, this, [this]() {
|
connect(hide_selected, &QToolButton::clicked, this, [this]() {
|
||||||
session_state_->setStatusMessage("Selection", "Hide selected coming soon");
|
session_state_->setStatusMessage("Selection", "Hide selected coming soon");
|
||||||
});
|
});
|
||||||
auto* isolate_selected = makeRibbonAction("Isolate", ":/icons/eye-solid.svg");
|
auto* isolate_selected = components::buttons::makeButton("Isolate", ":/icons/eye-solid.svg", this);
|
||||||
connect(isolate_selected, &QToolButton::clicked, this, [this]() {
|
connect(isolate_selected, &QToolButton::clicked, this, [this]() {
|
||||||
session_state_->setStatusMessage("Selection", "Isolate selected coming soon");
|
session_state_->setStatusMessage("Selection", "Isolate selected coming soon");
|
||||||
});
|
});
|
||||||
auto* show_all = makeRibbonAction("Show All", ":/icons/eye.svg");
|
auto* show_all = components::buttons::makeButton("Show All", ":/icons/eye.svg", this);
|
||||||
connect(show_all, &QToolButton::clicked, this, [this]() {
|
connect(show_all, &QToolButton::clicked, this, [this]() {
|
||||||
session_state_->setStatusMessage("Selection", "Show all coming soon");
|
session_state_->setStatusMessage("Selection", "Show all coming soon");
|
||||||
});
|
});
|
||||||
auto* invert_selection = makeRibbonAction("Invert", ":/icons/intersect.svg");
|
auto* invert_selection = components::buttons::makeButton("Invert", ":/icons/intersect.svg", this);
|
||||||
connect(invert_selection, &QToolButton::clicked, this, [this]() {
|
connect(invert_selection, &QToolButton::clicked, this, [this]() {
|
||||||
session_state_->setStatusMessage("Selection", "Invert selection coming soon");
|
session_state_->setStatusMessage("Selection", "Invert selection coming soon");
|
||||||
});
|
});
|
||||||
|
|
||||||
auto* distance = makeRibbonAction("Distance", ":/icons/select-edge3d.svg");
|
auto* distance = components::buttons::makeButton("Distance", ":/icons/select-edge3d.svg", this);
|
||||||
connect(distance, &QToolButton::clicked, this, [this]() {
|
connect(distance, &QToolButton::clicked, this, [this]() {
|
||||||
session_state_->setStatusMessage("Measure", "Distance coming soon");
|
session_state_->setStatusMessage("Measure", "Distance coming soon");
|
||||||
});
|
});
|
||||||
auto* area = makeRibbonAction("Area", ":/icons/select-face3d.svg");
|
auto* area = components::buttons::makeButton("Area", ":/icons/select-face3d.svg", this);
|
||||||
connect(area, &QToolButton::clicked, this, [this]() {
|
connect(area, &QToolButton::clicked, this, [this]() {
|
||||||
session_state_->setStatusMessage("Measure", "Area coming soon");
|
session_state_->setStatusMessage("Measure", "Area coming soon");
|
||||||
});
|
});
|
||||||
auto* volume = makeRibbonAction("Volume", ":/icons/select-point3d.svg");
|
auto* volume = components::buttons::makeButton("Volume", ":/icons/select-point3d.svg", this);
|
||||||
connect(volume, &QToolButton::clicked, this, [this]() {
|
connect(volume, &QToolButton::clicked, this, [this]() {
|
||||||
session_state_->setStatusMessage("Measure", "Volume coming soon");
|
session_state_->setStatusMessage("Measure", "Volume coming soon");
|
||||||
});
|
});
|
||||||
|
|
||||||
row->addWidget(makeRibbonGroup("SELECTION", {hide_selected, isolate_selected, show_all, invert_selection}));
|
row->addWidget(components::buttons::makeButtonGroup("SELECTION", {hide_selected, isolate_selected, show_all, invert_selection}, this));
|
||||||
row->addWidget(makeRibbonGroup("MEASURE", {distance, area, volume}));
|
row->addWidget(components::buttons::makeButtonGroup("MEASURE", {distance, area, volume}, this));
|
||||||
row->addStretch(1);
|
row->addStretch(1);
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
@@ -266,22 +277,22 @@ QWidget* MainWindow::buildPanelsRibbonPage() {
|
|||||||
row->setContentsMargins(2, 4, 2, 4);
|
row->setContentsMargins(2, 4, 2, 4);
|
||||||
row->setSpacing(0);
|
row->setSpacing(0);
|
||||||
|
|
||||||
row->addWidget(makeRibbonGroup("DATA", {
|
row->addWidget(components::buttons::makeButtonGroup("DATA", {
|
||||||
makePanelToggle("Models", models_panel_),
|
makePanelToggle("Models", models_panel_),
|
||||||
makePanelToggle("Spatial", spatial_panel_),
|
makePanelToggle("Spatial", spatial_panel_),
|
||||||
makePanelToggle("Layers", layers_panel_),
|
makePanelToggle("Layers", layers_panel_),
|
||||||
makePanelToggle("Properties", properties_panel_)
|
makePanelToggle("Properties", properties_panel_)
|
||||||
}));
|
}, this));
|
||||||
row->addWidget(makeRibbonGroup("QUERY", {
|
row->addWidget(components::buttons::makeButtonGroup("QUERY", {
|
||||||
makePanelToggle("Views", stored_views_panel_),
|
makePanelToggle("Views", stored_views_panel_),
|
||||||
makePanelToggle("Search", search_panel_),
|
makePanelToggle("Search", search_panel_),
|
||||||
makePanelToggle("Sheets", spreadsheet_panel_),
|
makePanelToggle("Sheets", spreadsheet_panel_),
|
||||||
makePanelToggle("Audit", audit_panel_)
|
makePanelToggle("Audit", audit_panel_)
|
||||||
}));
|
}, this));
|
||||||
row->addWidget(makeRibbonGroup("COLLABORATE", {
|
row->addWidget(components::buttons::makeButtonGroup("COLLABORATE", {
|
||||||
makePanelToggle("Clash", clash_panel_),
|
makePanelToggle("Clash", clash_panel_),
|
||||||
makePanelToggle("Issues", issues_panel_)
|
makePanelToggle("Issues", issues_panel_)
|
||||||
}));
|
}, this));
|
||||||
row->addStretch(1);
|
row->addStretch(1);
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
@@ -334,7 +345,7 @@ void MainWindow::setupPanels() {
|
|||||||
properties_panel_ = new panels::properties::PropertiesPanelWidget(this);
|
properties_panel_ = new panels::properties::PropertiesPanelWidget(this);
|
||||||
|
|
||||||
models_controller_ = new panels::models::ModelsPanelController(
|
models_controller_ = new panels::models::ModelsPanelController(
|
||||||
models_panel_, session_state_, viewport_widget_->viewport(), element_registry_, this);
|
this, models_panel_, session_state_, viewport_widget_->viewport(), element_registry_, this);
|
||||||
models_view_ = new panels::models::ModelsPanelView(models_panel_, session_state_, this);
|
models_view_ = new panels::models::ModelsPanelView(models_panel_, session_state_, this);
|
||||||
spatial_view_ = new panels::spatial_hierarchy::SpatialHierarchyPanelView(spatial_panel_, session_state_, this);
|
spatial_view_ = new panels::spatial_hierarchy::SpatialHierarchyPanelView(spatial_panel_, session_state_, this);
|
||||||
properties_view_ = new panels::properties::PropertiesPanelView(properties_panel_, session_state_, this);
|
properties_view_ = new panels::properties::PropertiesPanelView(properties_panel_, session_state_, this);
|
||||||
@@ -411,14 +422,12 @@ void MainWindow::setupLoader() {
|
|||||||
loader_ = new SceneLoader(viewport_widget_->viewport(), this);
|
loader_ = new SceneLoader(viewport_widget_->viewport(), this);
|
||||||
element_registry_->bindLoader(loader_);
|
element_registry_->bindLoader(loader_);
|
||||||
session_state_->bindLoader(loader_);
|
session_state_->bindLoader(loader_);
|
||||||
|
models_controller_->bindLoader(loader_);
|
||||||
viewport_controller_ = new panels::viewport::ViewportController(
|
viewport_controller_ = new panels::viewport::ViewportController(
|
||||||
session_state_, viewport_widget_->viewport(), this);
|
session_state_, viewport_widget_->viewport(), this);
|
||||||
connect(loader_, &SceneLoader::loadStarted, this, &MainWindow::onLoadStarted);
|
project_controller_ = new panels::project::ProjectController(
|
||||||
connect(loader_, &SceneLoader::loadedFromSidecar, this, &MainWindow::onLoadedFromSidecar);
|
this, federation_, session_state_, element_registry_,
|
||||||
connect(loader_, &SceneLoader::loadedFromStream, this, &MainWindow::onLoadedFromStream);
|
viewport_widget_->viewport(), models_controller_, viewport_controller_, this);
|
||||||
connect(loader_, &SceneLoader::loadCancelled, this, &MainWindow::onLoadCancelled);
|
|
||||||
connect(loader_, &SceneLoader::loadError, this, &MainWindow::onLoadError);
|
|
||||||
connect(loader_, &SceneLoader::allLoadsFinished, this, &MainWindow::onAllLoadsFinished);
|
|
||||||
|
|
||||||
connect(viewport_widget_->viewport(), &ViewportWindow::frameStatsUpdated, this,
|
connect(viewport_widget_->viewport(), &ViewportWindow::frameStatsUpdated, this,
|
||||||
[this](const ViewportWindow::FrameStats& s) {
|
[this](const ViewportWindow::FrameStats& s) {
|
||||||
@@ -440,204 +449,6 @@ void MainWindow::setupLoader() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::addFiles(const QStringList& paths) {
|
|
||||||
QStringList accepted_paths;
|
|
||||||
QStringList accepted_fed_ids;
|
|
||||||
for (const auto& path : paths) {
|
|
||||||
const QString fed_id = federation_->addModel(path);
|
|
||||||
if (fed_id.isEmpty()) continue;
|
|
||||||
accepted_paths << path;
|
|
||||||
accepted_fed_ids << fed_id;
|
|
||||||
}
|
|
||||||
loadModelsFromPaths(accepted_paths, accepted_fed_ids);
|
|
||||||
updateWindowTitle();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString MainWindow::formatElapsed(qint64 ms) const {
|
|
||||||
return (ms >= 1000)
|
|
||||||
? QString::number(ms / 1000.0, 'f', 2) + " s"
|
|
||||||
: QString::number(ms) + " ms";
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::onAddFiles() {
|
|
||||||
panels::add_model::AddModelDialog dialog(this);
|
|
||||||
if (dialog.exec() != QDialog::Accepted) return;
|
|
||||||
|
|
||||||
QStringList paths;
|
|
||||||
switch (dialog.selectedMode()) {
|
|
||||||
case panels::add_model::SourceMode::IfcFile: {
|
|
||||||
QFileDialog file_dialog(this, "Add IFC Files");
|
|
||||||
file_dialog.setFileMode(QFileDialog::ExistingFiles);
|
|
||||||
file_dialog.setNameFilter("IFC Files (*.ifc);;All Files (*)");
|
|
||||||
file_dialog.setOption(QFileDialog::DontUseNativeDialog, true);
|
|
||||||
if (file_dialog.exec() == QDialog::Accepted) {
|
|
||||||
paths = file_dialog.selectedFiles();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case panels::add_model::SourceMode::IfcDatabase: {
|
|
||||||
QFileDialog database_dialog(this, "Add IFC Databases");
|
|
||||||
database_dialog.setFileMode(QFileDialog::Directory);
|
|
||||||
database_dialog.setOption(QFileDialog::ShowDirsOnly, true);
|
|
||||||
database_dialog.setOption(QFileDialog::DontResolveSymlinks, true);
|
|
||||||
database_dialog.setOption(QFileDialog::DontUseNativeDialog, true);
|
|
||||||
if (auto* list = database_dialog.findChild<QListView*>("listView")) {
|
|
||||||
list->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
|
||||||
}
|
|
||||||
if (auto* tree = database_dialog.findChild<QTreeView*>()) {
|
|
||||||
tree->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
|
||||||
}
|
|
||||||
if (database_dialog.exec() == QDialog::Accepted) {
|
|
||||||
paths = database_dialog.selectedFiles();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case panels::add_model::SourceMode::GeometryOnly: {
|
|
||||||
QFileDialog file_dialog(this, "Add Geometry Only");
|
|
||||||
file_dialog.setFileMode(QFileDialog::ExistingFiles);
|
|
||||||
file_dialog.setNameFilter("IFC Viewer Cache (*.ifcview);;All Files (*)");
|
|
||||||
file_dialog.setOption(QFileDialog::DontUseNativeDialog, true);
|
|
||||||
if (file_dialog.exec() == QDialog::Accepted) {
|
|
||||||
paths = file_dialog.selectedFiles();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case panels::add_model::SourceMode::None:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
addFiles(paths);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::clearScene() {
|
|
||||||
if (viewport_widget_) viewport_widget_->viewport()->setSelectedObjectId(0);
|
|
||||||
session_state_->setSelectedObjectId(0);
|
|
||||||
session_state_->notifySelectionChanged();
|
|
||||||
|
|
||||||
const auto model_ids = session_state_->modelIds();
|
|
||||||
for (uint32_t mid : model_ids) {
|
|
||||||
viewport_widget_->viewport()->removeModel(mid);
|
|
||||||
loader_->removeModel(mid);
|
|
||||||
}
|
|
||||||
|
|
||||||
session_state_->clearModelMappings();
|
|
||||||
element_registry_->clear();
|
|
||||||
session_state_->notifyModelsChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MainWindow::confirmDiscardIfDirty() {
|
|
||||||
if (!federation_->isDirty()) return true;
|
|
||||||
const auto result = QMessageBox::question(
|
|
||||||
this, "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;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::loadModelsFromPaths(const QStringList& paths, const QStringList& fed_ids) {
|
|
||||||
if (paths.isEmpty()) return;
|
|
||||||
const auto ids = loader_->addFiles(paths);
|
|
||||||
for (int i = 0; i < paths.size() && i < static_cast<int>(ids.size()) && i < fed_ids.size(); ++i) {
|
|
||||||
session_state_->setModelMapping(fed_ids[i], ids[i]);
|
|
||||||
}
|
|
||||||
session_state_->notifyModelsChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MainWindow::openProject(const QString& path) {
|
|
||||||
if (loader_ && loader_->isLoading()) {
|
|
||||||
QMessageBox::information(
|
|
||||||
this, "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(this, "Open Project",
|
|
||||||
QString("Could not open project:\n%1").arg(err));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
clearScene();
|
|
||||||
|
|
||||||
QStringList paths;
|
|
||||||
QStringList fed_ids;
|
|
||||||
QStringList missing;
|
|
||||||
for (const auto& model : federation_->models()) {
|
|
||||||
if (model.source_kind != "local") continue;
|
|
||||||
if (!QFileInfo::exists(model.source_path)) {
|
|
||||||
missing << model.source_path;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
paths << model.source_path;
|
|
||||||
fed_ids << model.id;
|
|
||||||
}
|
|
||||||
loadModelsFromPaths(paths, fed_ids);
|
|
||||||
|
|
||||||
for (const auto& msg : missing) {
|
|
||||||
warnings << QString("Source not found, kept in project: %1").arg(msg);
|
|
||||||
}
|
|
||||||
if (!warnings.isEmpty()) {
|
|
||||||
QMessageBox::warning(this, "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_widget_->viewport()->setCamera(
|
|
||||||
hv.target.x(), hv.target.y(), hv.target.z(), hv.distance, hv.yaw, hv.pitch);
|
|
||||||
}
|
|
||||||
updateWindowTitle();
|
|
||||||
session_state_->setStatusMessage("Project", QFileInfo(path).fileName());
|
|
||||||
session_state_->notifyProjectOpened(path);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MainWindow::saveProject() {
|
|
||||||
if (federation_->filePath().isEmpty()) return saveProjectAs();
|
|
||||||
|
|
||||||
QString err;
|
|
||||||
if (!federation_->save(federation_->filePath(), &err)) {
|
|
||||||
QMessageBox::warning(this, "Save Project",
|
|
||||||
QString("Could not save project:\n%1").arg(err));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
updateWindowTitle();
|
|
||||||
session_state_->setStatusMessage("Project", QFileInfo(federation_->filePath()).fileName());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MainWindow::saveProjectAs() {
|
|
||||||
QString suggested = federation_->filePath();
|
|
||||||
if (suggested.isEmpty()) suggested = "project.ifcfed";
|
|
||||||
|
|
||||||
QFileDialog file_dialog(this, "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";
|
|
||||||
|
|
||||||
QString err;
|
|
||||||
if (!federation_->save(path, &err)) {
|
|
||||||
QMessageBox::warning(this, "Save Project",
|
|
||||||
QString("Could not save project:\n%1").arg(err));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
updateWindowTitle();
|
|
||||||
session_state_->setStatusMessage("Project", QFileInfo(path).fileName());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::updateWindowTitle() {
|
void MainWindow::updateWindowTitle() {
|
||||||
const QString project_path = federation_ ? federation_->filePath() : QString();
|
const QString project_path = federation_ ? federation_->filePath() : QString();
|
||||||
if (project_path.isEmpty() && (!federation_ || federation_->models().empty())) {
|
if (project_path.isEmpty() && (!federation_ || federation_->models().empty())) {
|
||||||
@@ -649,97 +460,4 @@ void MainWindow::updateWindowTitle() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onNewProject() {
|
|
||||||
if (loader_ && loader_->isLoading()) {
|
|
||||||
QMessageBox::information(
|
|
||||||
this, "New Project",
|
|
||||||
"Wait until the current model load finishes before creating a new project.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!confirmDiscardIfDirty()) return;
|
|
||||||
clearScene();
|
|
||||||
federation_->clear();
|
|
||||||
viewport_controller_->applyFederatedFalseOrigin();
|
|
||||||
updateWindowTitle();
|
|
||||||
session_state_->setStatusMessage("Project", "Untitled");
|
|
||||||
session_state_->notifyProjectReset();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::onOpenProject() {
|
|
||||||
QFileDialog file_dialog(this, "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;
|
|
||||||
const QString path = file_dialog.selectedFiles().value(0);
|
|
||||||
if (path.isEmpty()) return;
|
|
||||||
openProject(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::onSaveProject() {
|
|
||||||
saveProject();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::onSaveProjectAs() {
|
|
||||||
saveProjectAs();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::onSetHomeView() {
|
|
||||||
auto camera = viewport_widget_->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;
|
|
||||||
federation_->setHomeView(home_view);
|
|
||||||
updateWindowTitle();
|
|
||||||
session_state_->setStatusMessage("Camera", "Home view updated");
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::onGoHomeView() {
|
|
||||||
if (!federation_->hasHomeView()) {
|
|
||||||
session_state_->setStatusMessage("Camera", "No home view set for this project");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto& home_view = federation_->homeView();
|
|
||||||
viewport_widget_->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 MainWindow::onLoadStarted(uint32_t /*mid*/, QString display_name) {
|
|
||||||
session_state_->setStatusMessage("Loading", display_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::onLoadedFromSidecar(uint32_t mid, qint64 elapsed_ms) {
|
|
||||||
session_state_->setStatusMessage(
|
|
||||||
"Loaded",
|
|
||||||
QString("%1 from cache in %2")
|
|
||||||
.arg(loader_->displayName(mid))
|
|
||||||
.arg(formatElapsed(elapsed_ms)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::onLoadedFromStream(uint32_t mid, qint64 elapsed_ms) {
|
|
||||||
session_state_->setStatusMessage(
|
|
||||||
"Loaded",
|
|
||||||
QString("%1 streamed in %2")
|
|
||||||
.arg(loader_->displayName(mid))
|
|
||||||
.arg(formatElapsed(elapsed_ms)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::onLoadCancelled(uint32_t mid) {
|
|
||||||
session_state_->setStatusMessage("Cancelled", loader_->displayName(mid));
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::onLoadError(uint32_t /*mid*/, QString message) {
|
|
||||||
session_state_->setStatusMessage("Error", message);
|
|
||||||
QMessageBox::warning(this, "IfcInterfaceMockup", message);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::onAllLoadsFinished() {
|
|
||||||
session_state_->setStatusMessage("Loaded", QString("%1 model(s)").arg(loader_->modelCount()));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ifcinterface::shell
|
} // namespace ifcinterface::shell
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ namespace ifcinterface::components { class TabBar; }
|
|||||||
namespace ifcinterface::panels::models { class ModelsPanelController; }
|
namespace ifcinterface::panels::models { class ModelsPanelController; }
|
||||||
namespace ifcinterface::panels::models { class ModelsPanelWidget; }
|
namespace ifcinterface::panels::models { class ModelsPanelWidget; }
|
||||||
namespace ifcinterface::panels::models { class ModelsPanelView; }
|
namespace ifcinterface::panels::models { class ModelsPanelView; }
|
||||||
|
namespace ifcinterface::panels::project { class ProjectController; }
|
||||||
namespace ifcinterface::panels::spatial_hierarchy { class SpatialHierarchyPanelWidget; }
|
namespace ifcinterface::panels::spatial_hierarchy { class SpatialHierarchyPanelWidget; }
|
||||||
namespace ifcinterface::panels::spatial_hierarchy { class SpatialHierarchyPanelView; }
|
namespace ifcinterface::panels::spatial_hierarchy { class SpatialHierarchyPanelView; }
|
||||||
namespace ifcinterface::panels::properties { class PropertiesPanelWidget; }
|
namespace ifcinterface::panels::properties { class PropertiesPanelWidget; }
|
||||||
@@ -62,33 +63,8 @@ private:
|
|||||||
QWidget* buildNavigateRibbonPage();
|
QWidget* buildNavigateRibbonPage();
|
||||||
QWidget* buildInspectRibbonPage();
|
QWidget* buildInspectRibbonPage();
|
||||||
QWidget* buildPanelsRibbonPage();
|
QWidget* buildPanelsRibbonPage();
|
||||||
void clearScene();
|
|
||||||
bool confirmDiscardIfDirty();
|
|
||||||
void loadModelsFromPaths(const QStringList& paths, const QStringList& fed_ids);
|
|
||||||
bool openProject(const QString& path);
|
|
||||||
bool saveProject();
|
|
||||||
bool saveProjectAs();
|
|
||||||
void updateWindowTitle();
|
void updateWindowTitle();
|
||||||
QToolButton* makeRibbonAction(const QString& text, const QString& icon_path);
|
|
||||||
QWidget* makeRibbonGroup(const QString& title, const QList<QToolButton*>& buttons);
|
|
||||||
QToolButton* makePanelToggle(const QString& text, QDockWidget* dock);
|
QToolButton* makePanelToggle(const QString& text, QDockWidget* dock);
|
||||||
void addFiles(const QStringList& paths);
|
|
||||||
QString formatElapsed(qint64 ms) const;
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void onAddFiles();
|
|
||||||
void onLoadStarted(uint32_t mid, QString display_name);
|
|
||||||
void onLoadedFromSidecar(uint32_t mid, qint64 elapsed_ms);
|
|
||||||
void onLoadedFromStream(uint32_t mid, qint64 elapsed_ms);
|
|
||||||
void onLoadCancelled(uint32_t mid);
|
|
||||||
void onLoadError(uint32_t mid, QString message);
|
|
||||||
void onAllLoadsFinished();
|
|
||||||
void onSetHomeView();
|
|
||||||
void onGoHomeView();
|
|
||||||
void onNewProject();
|
|
||||||
void onOpenProject();
|
|
||||||
void onSaveProject();
|
|
||||||
void onSaveProjectAs();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Federation* federation_ = nullptr;
|
Federation* federation_ = nullptr;
|
||||||
@@ -114,6 +90,7 @@ private:
|
|||||||
QDockWidget* issues_panel_ = nullptr;
|
QDockWidget* issues_panel_ = nullptr;
|
||||||
ifcinterface::panels::models::ModelsPanelController* models_controller_ = nullptr;
|
ifcinterface::panels::models::ModelsPanelController* models_controller_ = nullptr;
|
||||||
ifcinterface::panels::models::ModelsPanelView* models_view_ = nullptr;
|
ifcinterface::panels::models::ModelsPanelView* models_view_ = nullptr;
|
||||||
|
ifcinterface::panels::project::ProjectController* project_controller_ = nullptr;
|
||||||
ifcinterface::panels::spatial_hierarchy::SpatialHierarchyPanelView* spatial_view_ = nullptr;
|
ifcinterface::panels::spatial_hierarchy::SpatialHierarchyPanelView* spatial_view_ = nullptr;
|
||||||
ifcinterface::panels::properties::PropertiesPanelView* properties_view_ = nullptr;
|
ifcinterface::panels::properties::PropertiesPanelView* properties_view_ = nullptr;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -100,6 +100,10 @@ void SessionState::notifyProjectOpened(const QString& path) {
|
|||||||
emit projectOpened(path);
|
emit projectOpened(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SessionState::notifyProjectSaved(const QString& path) {
|
||||||
|
emit projectSaved(path);
|
||||||
|
}
|
||||||
|
|
||||||
void SessionState::notifyProjectReset() {
|
void SessionState::notifyProjectReset() {
|
||||||
emit projectReset();
|
emit projectReset();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,10 +64,12 @@ public:
|
|||||||
void notifyFederationStructureChanged();
|
void notifyFederationStructureChanged();
|
||||||
void notifyVisibilityChanged();
|
void notifyVisibilityChanged();
|
||||||
void notifyProjectOpened(const QString& path);
|
void notifyProjectOpened(const QString& path);
|
||||||
|
void notifyProjectSaved(const QString& path);
|
||||||
void notifyProjectReset();
|
void notifyProjectReset();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void projectOpened(const QString& path);
|
void projectOpened(const QString& path);
|
||||||
|
void projectSaved(const QString& path);
|
||||||
void projectReset();
|
void projectReset();
|
||||||
void modelsChanged();
|
void modelsChanged();
|
||||||
void federationStructureChanged();
|
void federationStructureChanged();
|
||||||
|
|||||||
@@ -32,15 +32,14 @@ namespace ifcinterface::components::buttons {
|
|||||||
|
|
||||||
QToolButton* makeButton(const QString& text,
|
QToolButton* makeButton(const QString& text,
|
||||||
const QString& icon_path,
|
const QString& icon_path,
|
||||||
QWidget* parent,
|
QWidget* parent) {
|
||||||
const QSize& minimum_size) {
|
|
||||||
auto* button = new QToolButton(parent);
|
auto* button = new QToolButton(parent);
|
||||||
button->setObjectName("ribbonButton");
|
button->setObjectName("ribbonButton");
|
||||||
button->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
|
button->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
|
||||||
button->setIcon(components::icons::makeTintedSvgIcon(icon_path));
|
button->setIcon(components::icons::makeTintedSvgIcon(icon_path));
|
||||||
button->setIconSize(QSize(20, 20));
|
button->setIconSize(QSize(20, 20));
|
||||||
button->setText(text);
|
button->setText(text);
|
||||||
button->setMinimumSize(minimum_size);
|
button->setMinimumSize(QSize(90, 68));
|
||||||
button->setAutoRaise(false);
|
button->setAutoRaise(false);
|
||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
#define IFCINTERFACE_COMPONENTS_BUTTONS_H
|
#define IFCINTERFACE_COMPONENTS_BUTTONS_H
|
||||||
|
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QSize>
|
|
||||||
|
|
||||||
class QToolButton;
|
class QToolButton;
|
||||||
class QWidget;
|
class QWidget;
|
||||||
@@ -31,8 +30,7 @@ namespace ifcinterface::components::buttons {
|
|||||||
|
|
||||||
QToolButton* makeButton(const QString& text,
|
QToolButton* makeButton(const QString& text,
|
||||||
const QString& icon_path,
|
const QString& icon_path,
|
||||||
QWidget* parent,
|
QWidget* parent);
|
||||||
const QSize& minimum_size = QSize(90, 54));
|
|
||||||
|
|
||||||
QWidget* makeButtonGroup(const QString& title,
|
QWidget* makeButtonGroup(const QString& title,
|
||||||
const QList<QToolButton*>& buttons,
|
const QList<QToolButton*>& buttons,
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ void AddModelDialog::setupUi() {
|
|||||||
row->setContentsMargins(0, 0, 0, 0);
|
row->setContentsMargins(0, 0, 0, 0);
|
||||||
row->setSpacing(components::style::metrics::padding);
|
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]() {
|
connect(add_ifc, &QToolButton::clicked, this, [this]() {
|
||||||
selected_mode_ = SourceMode::IfcFile;
|
selected_mode_ = SourceMode::IfcFile;
|
||||||
accept();
|
accept();
|
||||||
@@ -100,7 +100,7 @@ void AddModelDialog::setupUi() {
|
|||||||
"Add IFC files and load both geometry and data.",
|
"Add IFC files and load both geometry and data.",
|
||||||
default_description));
|
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]() {
|
connect(add_database, &QToolButton::clicked, this, [this]() {
|
||||||
selected_mode_ = SourceMode::IfcDatabase;
|
selected_mode_ = SourceMode::IfcDatabase;
|
||||||
accept();
|
accept();
|
||||||
@@ -110,7 +110,7 @@ void AddModelDialog::setupUi() {
|
|||||||
"Add IFC RDB databases for optimised performance",
|
"Add IFC RDB databases for optimised performance",
|
||||||
default_description));
|
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]() {
|
connect(add_geometry, &QToolButton::clicked, this, [this]() {
|
||||||
selected_mode_ = SourceMode::GeometryOnly;
|
selected_mode_ = SourceMode::GeometryOnly;
|
||||||
accept();
|
accept();
|
||||||
@@ -120,7 +120,7 @@ void AddModelDialog::setupUi() {
|
|||||||
"Add pure geometry for fast visualisation",
|
"Add pure geometry for fast visualisation",
|
||||||
default_description));
|
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]() {
|
connect(convert_database, &QToolButton::clicked, this, [description]() {
|
||||||
description->setText("IFC-to-database conversion is coming soon.");
|
description->setText("IFC-to-database conversion is coming soon.");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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()));
|
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) {
|
void ViewportController::maybeGuessFederatedFalseOrigin(uint32_t mid) {
|
||||||
Federation* federation = session_state_->federation();
|
Federation* federation = session_state_->federation();
|
||||||
SceneLoader* loader = session_state_->loader();
|
SceneLoader* loader = session_state_->loader();
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ public:
|
|||||||
QObject* parent = nullptr);
|
QObject* parent = nullptr);
|
||||||
|
|
||||||
void applyFederatedFalseOrigin();
|
void applyFederatedFalseOrigin();
|
||||||
|
void setHomeView();
|
||||||
|
void goHomeView();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void applyCoordinateOperation(uint32_t mid);
|
void applyCoordinateOperation(uint32_t mid);
|
||||||
|
|||||||
Reference in New Issue
Block a user