Interface mockup 5

This commit is contained in:
Dion Moult
2026-05-06 10:47:03 +10:00
parent e20cea221b
commit 802ddf8ff9
18 changed files with 880 additions and 512 deletions
+8 -2
View File
@@ -25,14 +25,20 @@ find_package(Qt${QT_VERSION} COMPONENTS Core Gui Widgets Svg REQUIRED PATHS ${QT
set(INTERFACE_FILES
${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/ElementRegistry.cpp
${CMAKE_CURRENT_SOURCE_DIR}/ElementRegistry.h
${CMAKE_CURRENT_SOURCE_DIR}/MainWindow.cpp
${CMAKE_CURRENT_SOURCE_DIR}/MainWindow.h
${CMAKE_CURRENT_SOURCE_DIR}/components/SvgIcon.cpp
${CMAKE_CURRENT_SOURCE_DIR}/components/SvgIcon.h
${CMAKE_CURRENT_SOURCE_DIR}/components/Style.cpp
${CMAKE_CURRENT_SOURCE_DIR}/components/Style.h
${CMAKE_CURRENT_SOURCE_DIR}/components/KeyValueTable.cpp
${CMAKE_CURRENT_SOURCE_DIR}/components/KeyValueTable.h
${CMAKE_CURRENT_SOURCE_DIR}/components/Section.cpp
${CMAKE_CURRENT_SOURCE_DIR}/components/Section.h
${CMAKE_CURRENT_SOURCE_DIR}/components/PanelChrome.cpp
${CMAKE_CURRENT_SOURCE_DIR}/components/PanelChrome.h
${CMAKE_CURRENT_SOURCE_DIR}/components/Panel.cpp
${CMAKE_CURRENT_SOURCE_DIR}/components/Panel.h
${CMAKE_CURRENT_SOURCE_DIR}/panels/models/ModelsPanelTypes.h
${CMAKE_CURRENT_SOURCE_DIR}/panels/models/ModelsPanelWidget.cpp
${CMAKE_CURRENT_SOURCE_DIR}/panels/models/ModelsPanelWidget.h
+82
View File
@@ -0,0 +1,82 @@
// 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 "ElementRegistry.h"
#include "../ifcviewer/GeometryStreamer.h"
#include "../ifcviewer/SceneLoader.h"
#include "../ifcviewer/SidecarCache.h"
namespace ifcinterface {
ElementRegistry::ElementRegistry(QObject* parent)
: QObject(parent)
{
}
void ElementRegistry::bindLoader(SceneLoader* loader) {
connect(loader, &SceneLoader::sidecarElementsReady,
this, &ElementRegistry::onSidecarElementsReady);
connect(loader, &SceneLoader::streamedElementsReady,
this, &ElementRegistry::onStreamedElementsReady);
}
std::optional<BasicElementInfo> ElementRegistry::find(uint32_t object_id) const {
auto it = elements_.find(object_id);
if (it == elements_.end()) return std::nullopt;
return it->second;
}
void ElementRegistry::onSidecarElementsReady(uint32_t /*mid*/,
std::vector<PackedElementInfo> elements,
std::string string_table) {
auto str = [&](uint32_t offset, uint32_t length) -> QString {
if (length == 0 || offset + length > string_table.size()) return {};
return QString::fromStdString(string_table.substr(offset, length));
};
for (const auto& pe : elements) {
BasicElementInfo info;
info.object_id = pe.object_id;
info.model_id = pe.model_id;
info.ifc_id = pe.ifc_id;
info.parent_id = pe.parent_id;
info.guid = str(pe.guid_offset, pe.guid_length);
info.name = str(pe.name_offset, pe.name_length);
info.type = str(pe.type_offset, pe.type_length);
elements_[info.object_id] = info;
}
}
void ElementRegistry::onStreamedElementsReady(uint32_t /*mid*/, std::vector<ElementInfo> elements) {
for (const auto& e : elements) {
BasicElementInfo info;
info.object_id = e.object_id;
info.model_id = e.model_id;
info.ifc_id = e.ifc_id;
info.parent_id = e.parent_id;
info.guid = QString::fromStdString(e.guid);
info.name = QString::fromStdString(e.name);
info.type = QString::fromStdString(e.type);
elements_[info.object_id] = info;
}
}
} // namespace ifcinterface
+66
View File
@@ -0,0 +1,66 @@
// 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_ELEMENTREGISTRY_H
#define IFCINTERFACE_ELEMENTREGISTRY_H
#include <QObject>
#include <QString>
#include <optional>
#include <unordered_map>
#include <vector>
#include <string>
class SceneLoader;
struct PackedElementInfo;
struct ElementInfo;
namespace ifcinterface {
struct BasicElementInfo {
uint32_t object_id = 0;
uint32_t model_id = 0;
int ifc_id = 0;
int parent_id = 0;
QString guid;
QString name;
QString type;
};
class ElementRegistry : public QObject {
Q_OBJECT
public:
explicit ElementRegistry(QObject* parent = nullptr);
void bindLoader(SceneLoader* loader);
std::optional<BasicElementInfo> find(uint32_t object_id) const;
private:
void onSidecarElementsReady(uint32_t mid,
std::vector<PackedElementInfo> elements,
std::string string_table);
void onStreamedElementsReady(uint32_t mid, std::vector<ElementInfo> elements);
std::unordered_map<uint32_t, BasicElementInfo> elements_;
};
} // namespace ifcinterface
#endif
+69 -342
View File
@@ -23,7 +23,9 @@
#include "../ifcviewer/AppSettings.h"
#include "../ifcviewer/SceneLoader.h"
#include "../ifcviewer/ViewportWindow.h"
#include "components/PanelChrome.h"
#include "ElementRegistry.h"
#include "components/Panel.h"
#include "components/Style.h"
#include "components/SvgIcon.h"
#include "panels/todo/TodoPanelWidget.h"
#include "panels/models/ModelsPanelView.h"
@@ -52,9 +54,10 @@ namespace ifcinterface::shell {
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent)
{
element_registry_ = new ifcinterface::ElementRegistry(this);
setupChrome();
setupViewport();
setupDocks();
setupPanels();
setupStatus();
setupLoader();
setupRibbon();
@@ -66,280 +69,7 @@ void MainWindow::setupChrome() {
setDockOptions(QMainWindow::AllowNestedDocks |
QMainWindow::AllowTabbedDocks |
QMainWindow::GroupedDragging);
setStyleSheet(R"(
QMainWindow {
background: #26292f;
}
QWidget {
color: #d0d5dd;
background: #26292f;
selection-background-color: #39b54a;
selection-color: #14161a;
}
QFrame#ribbonShell {
background: #2d3138;
border-bottom: 1px solid #1b1d22;
}
QTabBar::tab {
background: transparent;
color: #8d97a7;
padding: 8px 14px;
margin-right: 2px;
border-bottom: 2px solid transparent;
}
QTabBar::tab:selected {
color: #f2f5fa;
border-bottom: 2px solid #39b54a;
}
QTabBar::tab:hover {
color: #ffffff;
}
QFrame#ribbonBand {
background: #31353d;
border-top: 1px solid #3b4048;
}
QFrame#ribbonPage {
background: transparent;
}
QFrame#ribbonGroup {
background: transparent;
border-right: 1px solid #434852;
}
QLabel#ribbonGroupLabel {
color: #7f8796;
font-size: 9px;
font-weight: 600;
letter-spacing: 0.08em;
}
QToolButton#ribbonButton {
background: transparent;
border: none;
padding: 6px 4px 4px 4px;
font-size: 11px;
color: #d6dce6;
}
QToolButton#ribbonButton:hover {
background: #3a3f48;
}
QToolButton#ribbonButton:pressed {
background: #24282f;
}
QFrame#viewportShell {
background: #202329;
border-top: 1px solid #1d2025;
}
QFrame#viewportFrame {
background: #1a1d22;
border: 1px solid #333942;
}
QDockWidget {
color: #d0d5dd;
}
QLabel#dockTitleText {
color: #dfe4ec;
font-size: 10px;
font-weight: 700;
letter-spacing: 0.08em;
}
QToolButton#dockTitleButton {
color: #8e97a5;
border: none;
background: transparent;
}
QToolButton#dockTitleButton:hover {
color: #ffffff;
background: #353a42;
}
QFrame#panelFrame {
background: #2b2f36;
border: 1px solid #3e444e;
border-radius: 3px;
}
QWidget#panelBody {
background: #2b2f36;
}
QTreeWidget, QListWidget, QTableWidget, QAbstractScrollArea {
background: #2b2f36;
border: none;
outline: none;
gridline-color: #333842;
}
QTreeWidget::viewport, QListWidget::viewport, QTableWidget::viewport {
background: #2b2f36;
}
QHeaderView::section {
background: #31353d;
color: #b5becc;
border: none;
border-bottom: 1px solid #434a55;
padding: 7px 8px;
font-weight: 600;
}
QTableCornerButton::section {
background: #31353d;
border: none;
}
QScrollArea {
background: #2b2f36;
border: none;
}
QScrollArea > QWidget > QWidget {
background: #2b2f36;
}
QLineEdit {
background: #31353d;
border: 1px solid #434a55;
border-radius: 3px;
padding: 6px 8px;
color: #d9dfeb;
}
QLineEdit:focus {
border: 1px solid #5b6472;
}
QFrame#entityClassCard {
background: #26292f;
border: 1px solid #404650;
border-radius: 3px;
}
QLabel#entityClassLabel {
color: #eef2f8;
font-weight: 700;
background: transparent;
}
QLabel#entityTypeLabel {
color: #9aa4b3;
background: transparent;
}
QWidget#attributeList {
background: transparent;
}
QTreeView::item, QListView::item, QTableView::item {
padding: 4px;
}
QScrollBar:vertical {
background: transparent;
width: 10px;
margin: 2px 2px 2px 0;
}
QScrollBar:horizontal {
background: transparent;
height: 10px;
margin: 0 2px 2px 2px;
}
QScrollBar::handle:vertical, QScrollBar::handle:horizontal {
background: #525a67;
border-radius: 3px;
min-height: 24px;
min-width: 24px;
}
QScrollBar::handle:vertical:hover, QScrollBar::handle:horizontal:hover {
background: #697385;
}
QScrollBar::add-line, QScrollBar::sub-line,
QScrollBar::add-page, QScrollBar::sub-page {
background: transparent;
border: none;
}
QStatusBar {
background: #24272c;
border-top: 1px solid #1a1c20;
}
QStatusBar QLabel {
color: #97a1af;
background: transparent;
border: none;
padding: 2px 8px;
}
QGroupBox {
background: transparent;
border: 1px solid #404650;
border-radius: 3px;
margin-top: 10px;
padding-top: 10px;
}
QGroupBox#propertySetCard {
background: #26292f;
border: 1px solid #404650;
border-radius: 3px;
}
QGroupBox#propertySetCard::title {
subcontrol-origin: margin;
left: 10px;
padding: 0 4px;
color: #d5dbe5;
}
QGroupBox#propertySetCard > QWidget {
background: #26292f;
}
QWidget#panelSection {
background: transparent;
}
QWidget#panelSectionFilterWrapper {
background: transparent;
}
QWidget#relationshipRow {
background: transparent;
}
QLabel#relationshipIconLabel {
background: transparent;
}
QWidget#inspectorPanel {
background: #2b2f36;
}
QFrame#panelSectionHeader {
background: #26292f;
}
QToolButton#panelSectionHeaderButton {
background: transparent;
border: none;
color: #e1e7f0;
font-weight: 700;
text-align: left;
padding: 2px;
margin: 0;
}
QToolButton#panelSectionHeaderButton:hover {
color: #ffffff;
}
QToolButton#panelSectionHeaderButton::menu-indicator {
image: none;
width: 0;
}
QToolButton#panelSectionFilterToggle {
background: transparent;
border: none;
padding: 2px;
}
QToolButton#panelSectionFilterToggle:hover {
background: #353a42;
}
QWidget#panelSectionBody {
background: transparent;
}
QLabel#propertyKeyLabel {
color: #9aa4b3;
background: transparent;
}
QLabel#propertyValueLabel {
color: #dce2eb;
background: transparent;
}
QLabel#relationshipValueLabel {
color: #dce2eb;
background: transparent;
}
QLabel#todoPanelTitle {
font-size: 14px;
font-weight: 600;
color: #e1e6ee;
background: transparent;
}
QLabel#todoPanelBody {
color: #8f98a6;
background: transparent;
}
)");
setStyleSheet(components::style::buildAppStyleSheet());
}
QToolButton* MainWindow::makeRibbonAction(const QString& text, const QString& icon_path) {
@@ -376,17 +106,13 @@ QWidget* MainWindow::makeRibbonGroup(const QString& title, const QList<QToolButt
return group;
}
QWidget* MainWindow::makeComingSoonPanel(const QString& title) {
return new panels::todo::TodoPanelWidget(title, this);
}
void MainWindow::setStatusMessage(const QString& mode, const QString& detail) {
status_mode_label_->setText(mode);
status_selection_label_->setText(detail);
}
QToolButton* MainWindow::makePanelToggle(const QString& text, QDockWidget* dock) {
auto* button = makeRibbonAction(text, ":/icons/dm_toggle_openings.png");
auto* button = makeRibbonAction(text, ":/icons/sidebar-expand.svg");
button->setCheckable(true);
button->setChecked(dock->isVisible());
connect(button, &QToolButton::toggled, dock, [dock](bool checked) {
@@ -565,19 +291,20 @@ QWidget* MainWindow::buildPanelsRibbonPage() {
row->setSpacing(0);
row->addWidget(makeRibbonGroup("DATA", {
makePanelToggle("Models", models_dock_),
makePanelToggle("Spatial", spatial_dock_),
makePanelToggle("Layers", layers_dock_),
makePanelToggle("Properties", properties_dock_)
makePanelToggle("Models", models_panel_),
makePanelToggle("Spatial", spatial_panel_),
makePanelToggle("Layers", layers_panel_),
makePanelToggle("Properties", properties_panel_)
}));
row->addWidget(makeRibbonGroup("QUERY", {
makePanelToggle("Views", stored_views_dock_),
makePanelToggle("Search", search_dock_),
makePanelToggle("Sheets", spreadsheet_dock_)
makePanelToggle("Views", stored_views_panel_),
makePanelToggle("Search", search_panel_),
makePanelToggle("Sheets", spreadsheet_panel_),
makePanelToggle("Audit", audit_panel_)
}));
row->addWidget(makeRibbonGroup("COLLABORATE", {
makePanelToggle("Clash", clash_dock_),
makePanelToggle("Issues", issues_dock_)
makePanelToggle("Clash", clash_panel_),
makePanelToggle("Issues", issues_panel_)
}));
row->addStretch(1);
return page;
@@ -644,68 +371,67 @@ void MainWindow::setupViewport() {
setCentralWidget(shell);
}
void MainWindow::setupDocks() {
auto* models_panel = new panels::models::ModelsPanelWidget(this);
auto* spatial_panel = new panels::spatial_hierarchy::SpatialHierarchyPanelWidget(this);
auto* properties_panel = new panels::properties::PropertiesPanelWidget(this);
void MainWindow::setupPanels() {
auto* models_widget = new panels::models::ModelsPanelWidget(this);
auto* spatial_widget = new panels::spatial_hierarchy::SpatialHierarchyPanelWidget(this);
auto* properties_widget = new panels::properties::PropertiesPanelWidget(this);
models_panel_view_ = new panels::models::ModelsPanelView(models_panel, this);
spatial_panel_view_ = new panels::spatial_hierarchy::SpatialHierarchyPanelView(spatial_panel, this);
properties_panel_view_ = new panels::properties::PropertiesPanelView(properties_panel, this);
models_view_ = new panels::models::ModelsPanelView(models_widget, this);
spatial_view_ = new panels::spatial_hierarchy::SpatialHierarchyPanelView(spatial_widget, this);
properties_view_ = new panels::properties::PropertiesPanelView(
properties_widget, viewport_, element_registry_, this);
connect(models_panel_view_, &panels::models::ModelsPanelView::statusMessageRequested,
connect(models_view_, &panels::models::ModelsPanelView::statusMessageRequested,
this, &MainWindow::setStatusMessage);
connect(spatial_panel_view_, &panels::spatial_hierarchy::SpatialHierarchyPanelView::statusMessageRequested,
connect(spatial_view_, &panels::spatial_hierarchy::SpatialHierarchyPanelView::statusMessageRequested,
this, &MainWindow::setStatusMessage);
models_dock_ = components::panel::makeDock(
"Models", components::panel::wrapPanel(models_panel), this, true);
spatial_dock_ = components::panel::makeDock(
"Spatial Hierarchy", components::panel::wrapPanel(spatial_panel), this);
properties_dock_ = components::panel::makeDock(
"Properties", components::panel::wrapPanel(properties_panel), this);
layers_dock_ = components::panel::makeDock(
"Layers", components::panel::wrapPanel(makeComingSoonPanel("Layers")), this);
stored_views_dock_ = components::panel::makeDock(
"Stored Views", components::panel::wrapPanel(makeComingSoonPanel("Stored Views")), this);
search_dock_ = components::panel::makeDock(
"Search and Query", components::panel::wrapPanel(makeComingSoonPanel("Search and Query")), this);
spreadsheet_dock_ = components::panel::makeDock(
"Spreadsheet", components::panel::wrapPanel(makeComingSoonPanel("Spreadsheet")), this);
clash_dock_ = components::panel::makeDock(
"Clash", components::panel::wrapPanel(makeComingSoonPanel("Clash")), this);
issues_dock_ = components::panel::makeDock(
"Issues", components::panel::wrapPanel(makeComingSoonPanel("Issues")), this);
models_panel_ = new components::Panel("Models", models_widget, this, true);
spatial_panel_ = new components::Panel("Spatial Hierarchy", spatial_widget, this);
properties_panel_ = new components::Panel("Properties", properties_widget, this);
layers_panel_ = new components::Panel("Layers", new panels::todo::TodoPanelWidget("Layers", this), this);
stored_views_panel_ = new components::Panel(
"Stored Views", new panels::todo::TodoPanelWidget("Stored Views", this), this);
search_panel_ = new components::Panel(
"Search and Query", new panels::todo::TodoPanelWidget("Search and Query", this), this);
spreadsheet_panel_ = new components::Panel(
"Spreadsheet", new panels::todo::TodoPanelWidget("Spreadsheet", this), this);
audit_panel_ = new components::Panel("Audit", new panels::todo::TodoPanelWidget("Audit", this), this);
clash_panel_ = new components::Panel("Clash", new panels::todo::TodoPanelWidget("Clash", this), this);
issues_panel_ = new components::Panel("Issues", new panels::todo::TodoPanelWidget("Issues", this), this);
addDockWidget(Qt::LeftDockWidgetArea, models_dock_);
addDockWidget(Qt::LeftDockWidgetArea, spatial_dock_);
splitDockWidget(models_dock_, spatial_dock_, Qt::Vertical);
addDockWidget(Qt::LeftDockWidgetArea, models_panel_);
addDockWidget(Qt::LeftDockWidgetArea, spatial_panel_);
splitDockWidget(models_panel_, spatial_panel_, Qt::Vertical);
addDockWidget(Qt::RightDockWidgetArea, properties_dock_);
addDockWidget(Qt::RightDockWidgetArea, layers_dock_);
addDockWidget(Qt::RightDockWidgetArea, stored_views_dock_);
addDockWidget(Qt::RightDockWidgetArea, search_dock_);
addDockWidget(Qt::RightDockWidgetArea, spreadsheet_dock_);
addDockWidget(Qt::RightDockWidgetArea, clash_dock_);
addDockWidget(Qt::RightDockWidgetArea, issues_dock_);
addDockWidget(Qt::RightDockWidgetArea, properties_panel_);
addDockWidget(Qt::RightDockWidgetArea, layers_panel_);
addDockWidget(Qt::RightDockWidgetArea, stored_views_panel_);
addDockWidget(Qt::RightDockWidgetArea, search_panel_);
addDockWidget(Qt::RightDockWidgetArea, spreadsheet_panel_);
addDockWidget(Qt::RightDockWidgetArea, audit_panel_);
addDockWidget(Qt::RightDockWidgetArea, clash_panel_);
addDockWidget(Qt::RightDockWidgetArea, issues_panel_);
tabifyDockWidget(properties_dock_, layers_dock_);
tabifyDockWidget(layers_dock_, stored_views_dock_);
tabifyDockWidget(stored_views_dock_, search_dock_);
tabifyDockWidget(search_dock_, spreadsheet_dock_);
tabifyDockWidget(spreadsheet_dock_, clash_dock_);
tabifyDockWidget(clash_dock_, issues_dock_);
properties_dock_->raise();
tabifyDockWidget(properties_panel_, layers_panel_);
tabifyDockWidget(layers_panel_, stored_views_panel_);
tabifyDockWidget(stored_views_panel_, search_panel_);
tabifyDockWidget(search_panel_, spreadsheet_panel_);
tabifyDockWidget(spreadsheet_panel_, audit_panel_);
tabifyDockWidget(audit_panel_, clash_panel_);
tabifyDockWidget(clash_panel_, issues_panel_);
properties_panel_->raise();
layers_dock_->hide();
stored_views_dock_->hide();
search_dock_->hide();
spreadsheet_dock_->hide();
clash_dock_->hide();
issues_dock_->hide();
layers_panel_->hide();
stored_views_panel_->hide();
search_panel_->hide();
spreadsheet_panel_->hide();
audit_panel_->hide();
clash_panel_->hide();
issues_panel_->hide();
resizeDocks({models_dock_, properties_dock_}, {290, 330}, Qt::Horizontal);
resizeDocks({models_dock_, spatial_dock_}, {280, 240}, Qt::Vertical);
resizeDocks({models_panel_, properties_panel_}, {290, 330}, Qt::Horizontal);
resizeDocks({models_panel_, spatial_panel_}, {280, 240}, Qt::Vertical);
}
void MainWindow::setupStatus() {
@@ -728,6 +454,7 @@ void MainWindow::setupStatus() {
void MainWindow::setupLoader() {
AppSettings::instance().setLoadDataSource(false);
loader_ = new SceneLoader(viewport_, this);
element_registry_->bindLoader(loader_);
connect(loader_, &SceneLoader::loadStarted, this, &MainWindow::onLoadStarted);
connect(loader_, &SceneLoader::loadedFromSidecar, this, &MainWindow::onLoadedFromSidecar);
connect(loader_, &SceneLoader::loadedFromStream, this, &MainWindow::onLoadedFromStream);
+16 -14
View File
@@ -31,6 +31,7 @@ class QTabBar;
class QToolButton;
class ViewportWindow;
class SceneLoader;
namespace ifcinterface { class ElementRegistry; }
namespace ifcinterface::panels::models { class ModelsPanelView; }
namespace ifcinterface::panels::spatial_hierarchy { class SpatialHierarchyPanelView; }
namespace ifcinterface::panels::properties { class PropertiesPanelView; }
@@ -46,7 +47,7 @@ private:
void setupChrome();
void setupRibbon();
void setupViewport();
void setupDocks();
void setupPanels();
void setupStatus();
void setupLoader();
QWidget* buildHomeRibbonPage();
@@ -55,7 +56,6 @@ private:
QWidget* buildPanelsRibbonPage();
QToolButton* makeRibbonAction(const QString& text, const QString& icon_path);
QWidget* makeRibbonGroup(const QString& title, const QList<QToolButton*>& buttons);
QWidget* makeComingSoonPanel(const QString& title);
QToolButton* makePanelToggle(const QString& text, QDockWidget* dock);
void setStatusMessage(const QString& mode, const QString& detail);
void addFiles(const QStringList& paths);
@@ -78,19 +78,21 @@ private:
QStackedWidget* ribbon_pages_ = nullptr;
ViewportWindow* viewport_ = nullptr;
SceneLoader* loader_ = nullptr;
ifcinterface::ElementRegistry* element_registry_ = nullptr;
QWidget* viewport_container_ = nullptr;
QDockWidget* models_dock_ = nullptr;
QDockWidget* spatial_dock_ = nullptr;
QDockWidget* layers_dock_ = nullptr;
QDockWidget* properties_dock_ = nullptr;
QDockWidget* stored_views_dock_ = nullptr;
QDockWidget* search_dock_ = nullptr;
QDockWidget* spreadsheet_dock_ = nullptr;
QDockWidget* clash_dock_ = nullptr;
QDockWidget* issues_dock_ = nullptr;
ifcinterface::panels::models::ModelsPanelView* models_panel_view_ = nullptr;
ifcinterface::panels::spatial_hierarchy::SpatialHierarchyPanelView* spatial_panel_view_ = nullptr;
ifcinterface::panels::properties::PropertiesPanelView* properties_panel_view_ = nullptr;
QDockWidget* models_panel_ = nullptr;
QDockWidget* spatial_panel_ = nullptr;
QDockWidget* layers_panel_ = nullptr;
QDockWidget* properties_panel_ = nullptr;
QDockWidget* stored_views_panel_ = nullptr;
QDockWidget* search_panel_ = nullptr;
QDockWidget* spreadsheet_panel_ = nullptr;
QDockWidget* audit_panel_ = nullptr;
QDockWidget* clash_panel_ = nullptr;
QDockWidget* issues_panel_ = nullptr;
ifcinterface::panels::models::ModelsPanelView* models_view_ = nullptr;
ifcinterface::panels::spatial_hierarchy::SpatialHierarchyPanelView* spatial_view_ = nullptr;
ifcinterface::panels::properties::PropertiesPanelView* properties_view_ = nullptr;
};
} // namespace ifcinterface::shell
@@ -0,0 +1,82 @@
// 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 "KeyValueTable.h"
#include "SvgIcon.h"
#include <QHBoxLayout>
#include <QLabel>
#include <QVBoxLayout>
namespace ifcinterface::components {
KeyValueTable::KeyValueTable(const QList<KeyValueTableRow>& rows, QWidget* parent)
: QWidget(parent)
{
setObjectName("attributeList");
auto* layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(6);
for (const auto& row_data : rows) {
auto* row = new QWidget(this);
if (!row_data.trailing_icon_path.isEmpty()) {
row->setObjectName("relationshipRow");
} else {
row->setObjectName("keyValueRow");
}
auto* row_layout = new QHBoxLayout(row);
row_layout->setContentsMargins(0, 0, 0, 0);
row_layout->setSpacing(12);
auto* key = new QLabel(row_data.key, row);
key->setObjectName("propertyKeyLabel");
if (row_data.key_minimum_width > 0) {
key->setMinimumWidth(row_data.key_minimum_width);
}
auto* value = new QLabel(row_data.value, row);
value->setObjectName(row_data.value_object_name.isEmpty()
? "propertyValueLabel"
: row_data.value_object_name);
value->setWordWrap(true);
value->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
row_layout->addWidget(key);
row_layout->addWidget(value, 1);
if (!row_data.trailing_icon_path.isEmpty()) {
auto* icon = new QLabel(row);
icon->setObjectName(row_data.trailing_icon_object_name.isEmpty()
? "relationshipIconLabel"
: row_data.trailing_icon_object_name);
icon->setPixmap(icons::makePanelSvgPixmap(row_data.trailing_icon_path, QSize(14, 14)));
icon->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
row_layout->addWidget(icon, 0, Qt::AlignRight | Qt::AlignVCenter);
}
layout->addWidget(row);
}
}
} // namespace ifcinterface::components
+47
View File
@@ -0,0 +1,47 @@
// 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_COMPONENTS_KEYVALUETABLE_H
#define IFCINTERFACE_COMPONENTS_KEYVALUETABLE_H
#include <QList>
#include <QString>
#include <QWidget>
namespace ifcinterface::components {
struct KeyValueTableRow {
QString key;
QString value;
QString value_object_name = "propertyValueLabel";
QString trailing_icon_path;
QString trailing_icon_object_name;
int key_minimum_width = 0;
};
class KeyValueTable : public QWidget {
Q_OBJECT
public:
explicit KeyValueTable(const QList<KeyValueTableRow>& rows, QWidget* parent = nullptr);
};
} // namespace ifcinterface::components
#endif
@@ -18,8 +18,9 @@
* *
********************************************************************************/
#include "PanelChrome.h"
#include "Panel.h"
#include "Style.h"
#include "SvgIcon.h"
#include <QDockWidget>
@@ -30,7 +31,7 @@
#include <QToolButton>
#include <QVBoxLayout>
namespace ifcinterface::components::panel {
namespace ifcinterface::components {
namespace {
@@ -69,32 +70,31 @@ public:
} // namespace
QDockWidget* makeDock(const QString& title, QWidget* content, QWidget* parent, bool has_settings) {
auto* dock = new QDockWidget(title, parent);
dock->setObjectName(title);
dock->setFeatures(QDockWidget::DockWidgetMovable |
QDockWidget::DockWidgetFloatable |
QDockWidget::DockWidgetClosable);
dock->setTitleBarWidget(new DockTitleBar(title, has_settings, dock));
dock->setWidget(content);
return dock;
}
QFrame* wrapPanel(QWidget* inner) {
Panel::Panel(const QString& title, QWidget* content, QWidget* parent, bool has_settings)
: QDockWidget(title, parent)
{
auto* outer = new QFrame();
auto* outer_layout = new QVBoxLayout(outer);
outer_layout->setContentsMargins(6, 6, 6, 6);
outer_layout->setContentsMargins(style::metrics::padding,
style::metrics::padding,
style::metrics::padding,
style::metrics::padding);
outer_layout->setSpacing(0);
auto* frame = new QFrame(outer);
frame->setObjectName("panelFrame");
auto* layout = new QVBoxLayout(frame);
layout->setContentsMargins(0, 8, 0, 8);
layout->setSpacing(0);
layout->addWidget(inner);
frame->setObjectName("panel");
auto* frame_layout = new QVBoxLayout(frame);
frame_layout->setContentsMargins(0, style::metrics::padding, 0, style::metrics::padding);
frame_layout->setSpacing(0);
frame_layout->addWidget(content);
outer_layout->addWidget(frame);
return outer;
setObjectName(title);
setFeatures(QDockWidget::DockWidgetMovable |
QDockWidget::DockWidgetFloatable |
QDockWidget::DockWidgetClosable);
setTitleBarWidget(new DockTitleBar(title, has_settings, this));
setWidget(outer);
}
} // namespace ifcinterface::components::panel
} // namespace ifcinterface::components
@@ -21,17 +21,19 @@
#ifndef IFCINTERFACE_COMPONENTS_PANEL_PANELCHROME_H
#define IFCINTERFACE_COMPONENTS_PANEL_PANELCHROME_H
#include <QString>
#include <QDockWidget>
class QDockWidget;
class QFrame;
class QWidget;
namespace ifcinterface::components::panel {
namespace ifcinterface::components {
QDockWidget* makeDock(const QString& title, QWidget* content, QWidget* parent, bool has_settings = false);
QFrame* wrapPanel(QWidget* inner);
class Panel : public QDockWidget {
Q_OBJECT
} // namespace ifcinterface::components::panel
public:
explicit Panel(const QString& title, QWidget* content, QWidget* parent = nullptr, bool has_settings = false);
};
} // namespace ifcinterface::components
#endif
+13 -7
View File
@@ -20,6 +20,7 @@
#include "Section.h"
#include "Style.h"
#include "SvgIcon.h"
#include <QFrame>
@@ -49,14 +50,14 @@ Section::Section(const QString& title, SectionHeaderMode header_mode,
setObjectName("panelSection");
auto* layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(6);
layout->setSpacing(style::metrics::section_spacing);
if (header_mode == SectionHeaderMode::Visible) {
auto* header = new QFrame(this);
header->setObjectName("panelSectionHeader");
auto* header_layout = new QHBoxLayout(header);
header_layout->setContentsMargins(0, 0, 0, 0);
header_layout->setSpacing(6);
header_layout->setSpacing(style::metrics::section_spacing);
auto* toggle = new QToolButton(header);
toggle->setObjectName("panelSectionHeaderButton");
@@ -89,7 +90,8 @@ Section::Section(const QString& title, SectionHeaderMode header_mode,
auto* filter_wrapper = new QWidget(this);
filter_wrapper->setObjectName("panelSectionFilterWrapper");
auto* filter_wrapper_layout = new QVBoxLayout(filter_wrapper);
filter_wrapper_layout->setContentsMargins(10, 0, 10, 0);
filter_wrapper_layout->setContentsMargins(style::metrics::filter_body_padding_x, 0,
style::metrics::filter_body_padding_x, 0);
filter_wrapper_layout->setSpacing(0);
filter_wrapper_layout->addWidget(filter_field_);
layout->addWidget(filter_wrapper);
@@ -108,7 +110,8 @@ Section::Section(const QString& title, SectionHeaderMode header_mode,
auto* filter_wrapper = new QWidget(this);
filter_wrapper->setObjectName("panelSectionFilterWrapper");
auto* filter_wrapper_layout = new QVBoxLayout(filter_wrapper);
filter_wrapper_layout->setContentsMargins(8, 0, 8, 0);
filter_wrapper_layout->setContentsMargins(style::metrics::hidden_filter_body_padding_x, 0,
style::metrics::hidden_filter_body_padding_x, 0);
filter_wrapper_layout->setSpacing(0);
filter_wrapper_layout->addWidget(filter_field_);
layout->addWidget(filter_wrapper);
@@ -118,11 +121,14 @@ Section::Section(const QString& title, SectionHeaderMode header_mode,
body_->setObjectName("panelSectionBody");
body_layout_ = new QVBoxLayout(body_);
if (header_mode == SectionHeaderMode::Visible) {
body_layout_->setContentsMargins(10, 6, 10, 0);
body_layout_->setContentsMargins(style::metrics::section_body_padding_x,
style::metrics::section_body_padding_top,
style::metrics::section_body_padding_x, 0);
} else {
body_layout_->setContentsMargins(8, 0, 8, 0);
body_layout_->setContentsMargins(style::metrics::hidden_section_body_padding_x, 0,
style::metrics::hidden_section_body_padding_x, 0);
}
body_layout_->setSpacing(6);
body_layout_->setSpacing(style::metrics::section_spacing);
layout->addWidget(body_);
}
+303
View File
@@ -0,0 +1,303 @@
// 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 "Style.h"
namespace ifcinterface::components::style {
QString buildAppStyleSheet() {
return QString(R"(
QMainWindow {
background: #26292f;
}
QWidget {
color: #d0d5dd;
background: #26292f;
selection-background-color: #39b54a;
selection-color: #14161a;
}
QFrame#ribbonShell {
background: #2d3138;
border-bottom: 1px solid #1b1d22;
}
QTabBar::tab {
background: transparent;
color: #8d97a7;
padding: 8px 14px;
margin-right: 2px;
border-bottom: 2px solid transparent;
}
QTabBar::tab:selected {
color: #f2f5fa;
border-bottom: 2px solid #39b54a;
}
QTabBar::tab:hover {
color: #ffffff;
}
QFrame#ribbonBand {
background: #31353d;
border-top: 1px solid #3b4048;
}
QFrame#ribbonPage {
background: transparent;
}
QFrame#ribbonGroup {
background: transparent;
border-right: 1px solid #434852;
}
QLabel#ribbonGroupLabel {
color: #7f8796;
font-size: 9px;
font-weight: 600;
letter-spacing: 0.08em;
}
QToolButton#ribbonButton {
background: transparent;
border: none;
padding: 6px 4px 4px 4px;
font-size: 11px;
color: #d6dce6;
}
QToolButton#ribbonButton:hover {
background: #3a3f48;
}
QToolButton#ribbonButton:pressed {
background: #24282f;
}
QFrame#viewportShell {
background: #202329;
border-top: 1px solid #1d2025;
}
QFrame#viewportFrame {
background: #1a1d22;
border: 1px solid #333942;
}
QDockWidget {
color: #d0d5dd;
}
QLabel#dockTitleText {
color: #dfe4ec;
font-size: 10px;
font-weight: 700;
letter-spacing: 0.08em;
}
QToolButton#dockTitleButton {
color: #8e97a5;
border: none;
background: transparent;
}
QToolButton#dockTitleButton:hover {
color: #ffffff;
background: #353a42;
}
QFrame#panel {
background: #2b2f36;
border: 1px solid #3e444e;
border-radius: %1px;
}
QTreeWidget, QListWidget, QTableWidget, QAbstractScrollArea {
background: #2b2f36;
border: none;
outline: none;
gridline-color: #333842;
}
QTreeWidget::viewport, QListWidget::viewport, QTableWidget::viewport {
background: #2b2f36;
}
QHeaderView::section {
background: #31353d;
color: #b5becc;
border: none;
border-bottom: 1px solid #434a55;
padding: 7px 8px;
font-weight: 600;
}
QTableCornerButton::section {
background: #31353d;
border: none;
}
QScrollArea {
background: #2b2f36;
border: none;
}
QScrollArea > QWidget > QWidget {
background: #2b2f36;
}
QLineEdit {
background: #31353d;
border: 1px solid #434a55;
border-radius: %1px;
padding: %2px %3px;
color: #d9dfeb;
}
QLineEdit:focus {
border: 1px solid #5b6472;
}
QFrame#entityClassCard {
background: #26292f;
border: 1px solid #404650;
border-radius: %1px;
}
QLabel#entityClassLabel {
color: #eef2f8;
font-weight: 700;
background: transparent;
}
QLabel#entityTypeLabel {
color: #9aa4b3;
background: transparent;
}
QWidget#attributeList {
background: transparent;
}
QTreeView::item, QListView::item, QTableView::item {
padding: 4px;
}
QScrollBar:vertical {
background: transparent;
width: 10px;
margin: 2px 2px 2px 0;
}
QScrollBar:horizontal {
background: transparent;
height: 10px;
margin: 0 2px 2px 2px;
}
QScrollBar::handle:vertical, QScrollBar::handle:horizontal {
background: #525a67;
border-radius: %1px;
min-height: 24px;
min-width: 24px;
}
QScrollBar::handle:vertical:hover, QScrollBar::handle:horizontal:hover {
background: #697385;
}
QScrollBar::add-line, QScrollBar::sub-line,
QScrollBar::add-page, QScrollBar::sub-page {
background: transparent;
border: none;
}
QStatusBar {
background: #24272c;
border-top: 1px solid #1a1c20;
}
QStatusBar QLabel {
color: #97a1af;
background: transparent;
border: none;
padding: 2px 8px;
}
QGroupBox {
background: transparent;
border: 1px solid #404650;
border-radius: %1px;
margin-top: 10px;
padding-top: 10px;
}
QGroupBox#propertySetCard {
background: #26292f;
border: 1px solid #404650;
border-radius: %1px;
}
QGroupBox#propertySetCard::title {
subcontrol-origin: margin;
left: %4px;
padding: 0 4px;
color: #d5dbe5;
}
QGroupBox#propertySetCard > QWidget {
background: #26292f;
}
QWidget#panelSection {
background: transparent;
}
QWidget#panelSectionFilterWrapper {
background: transparent;
}
QWidget#relationshipRow {
background: transparent;
}
QLabel#relationshipIconLabel {
background: transparent;
}
QWidget#inspectorPanel {
background: #2b2f36;
}
QFrame#panelSectionHeader {
background: #26292f;
}
QToolButton#panelSectionHeaderButton {
background: transparent;
border: none;
color: #e1e7f0;
font-weight: 700;
text-align: left;
padding: %5px;
margin: 0;
}
QToolButton#panelSectionHeaderButton:hover {
color: #ffffff;
}
QToolButton#panelSectionHeaderButton::menu-indicator {
image: none;
width: 0;
}
QToolButton#panelSectionFilterToggle {
background: transparent;
border: none;
padding: %5px;
}
QToolButton#panelSectionFilterToggle:hover {
background: #353a42;
}
QWidget#panelSectionBody {
background: transparent;
}
QLabel#propertyKeyLabel {
color: #9aa4b3;
background: transparent;
}
QLabel#propertyValueLabel {
color: #dce2eb;
background: transparent;
}
QLabel#relationshipValueLabel {
color: #dce2eb;
background: transparent;
}
QLabel#todoPanelTitle {
font-size: 14px;
font-weight: 600;
color: #e1e6ee;
background: transparent;
}
QLabel#todoPanelBody {
color: #8f98a6;
background: transparent;
}
)")
.arg(metrics::panel_radius)
.arg(metrics::control_padding_y)
.arg(metrics::control_padding_x)
.arg(metrics::card_padding)
.arg(metrics::section_header_padding);
}
} // namespace ifcinterface::components::style
+49
View File
@@ -0,0 +1,49 @@
// 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_COMPONENTS_STYLE_H
#define IFCINTERFACE_COMPONENTS_STYLE_H
#include <QString>
namespace ifcinterface::components::style::metrics {
inline constexpr int padding = 6;
inline constexpr int section_spacing = 6;
inline constexpr int section_body_padding_x = 10;
inline constexpr int section_body_padding_top = 6;
inline constexpr int hidden_section_body_padding_x = 8;
inline constexpr int section_header_padding = 2;
inline constexpr int filter_body_padding_x = 10;
inline constexpr int hidden_filter_body_padding_x = 8;
inline constexpr int card_padding = 10;
inline constexpr int control_padding_y = 6;
inline constexpr int control_padding_x = 8;
inline constexpr int panel_radius = 3;
} // namespace ifcinterface::components::style::metrics
namespace ifcinterface::components::style {
QString buildAppStyleSheet();
} // namespace ifcinterface::components::style
#endif
+5
View File
@@ -0,0 +1,5 @@
<svg width="24" stroke-width="1.5" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M19 21L5 21C3.89543 21 3 20.1046 3 19L3 5C3 3.89543 3.89543 3 5 3L19 3C20.1046 3 21 3.89543 21 5L21 19C21 20.1046 20.1046 21 19 21Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.5 21V3" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.5 10L7.25 12L5.5 14" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 533 B

+1 -15
View File
@@ -4,21 +4,6 @@
<file alias="DMSans-VariableFont_opsz,wght.ttf">../ifctester/webapp/public/fonts/dmsans/DMSans-VariableFont_opsz,wght.ttf</file>
</qresource>
<qresource prefix="/icons">
<file alias="IFC.png">../bonsai/bonsai/bim/data/icons/IFC.png</file>
<file alias="dm_add.png">../bonsai/bonsai/bim/data/icons/dm_add.png</file>
<file alias="dm_add_type.png">../bonsai/bonsai/bim/data/icons/dm_add_type.png</file>
<file alias="dm_assign.png">../bonsai/bonsai/bim/data/icons/dm_assign.png</file>
<file alias="dm_centerline.png">../bonsai/bonsai/bim/data/icons/dm_centerline.png</file>
<file alias="dm_connect_mep_elements.png">../bonsai/bonsai/bim/data/icons/dm_connect_mep_elements.png</file>
<file alias="dm_decomposition.png">../bonsai/bonsai/bim/data/icons/dm_decomposition.png</file>
<file alias="dm_edit_profile.png">../bonsai/bonsai/bim/data/icons/dm_edit_profile.png</file>
<file alias="dm_ifc.png">../bonsai/bonsai/bim/data/icons/dm_ifc.png</file>
<file alias="dm_perform_quantity_take-off.png">../bonsai/bonsai/bim/data/icons/dm_perform_quantity_take-off.png</file>
<file alias="dm_rectangle.png">../bonsai/bonsai/bim/data/icons/dm_rectangle.png</file>
<file alias="dm_refresh.png">../bonsai/bonsai/bim/data/icons/dm_refresh.png</file>
<file alias="dm_rotate_90.png">../bonsai/bonsai/bim/data/icons/dm_rotate_90.png</file>
<file alias="dm_split.png">../bonsai/bonsai/bim/data/icons/dm_split.png</file>
<file alias="dm_toggle_openings.png">../bonsai/bonsai/bim/data/icons/dm_toggle_openings.png</file>
<file alias="plus-square.svg">icons/plus-square.svg</file>
<file alias="download-square.svg">icons/download-square.svg</file>
<file alias="cloud-square.svg">icons/cloud-square.svg</file>
@@ -55,5 +40,6 @@
<file alias="filter.svg">icons/filter.svg</file>
<file alias="cube-dots.svg">icons/cube-dots.svg</file>
<file alias="cursor-pointer.svg">icons/cursor-pointer.svg</file>
<file alias="sidebar-expand.svg">icons/sidebar-expand.svg</file>
</qresource>
</RCC>
@@ -22,22 +22,36 @@
#include "PropertiesPanelWidget.h"
#include "../../ElementRegistry.h"
#include "../../../ifcviewer/ViewportWindow.h"
namespace ifcinterface::panels::properties {
PropertiesPanelView::PropertiesPanelView(PropertiesPanelWidget* widget, QObject* parent)
: QObject(parent), widget_(widget)
PropertiesPanelView::PropertiesPanelView(PropertiesPanelWidget* widget,
ViewportWindow* viewport,
ifcinterface::ElementRegistry* registry,
QObject* parent)
: QObject(parent), widget_(widget), registry_(registry)
{
state_.entity = {"IfcWall", "SOLIDWALL"};
state_.attributes = {
connect(viewport, &ViewportWindow::objectPicked, this, [this](uint32_t object_id) {
refresh(object_id);
});
refresh(0);
}
void PropertiesPanelView::refresh(uint32_t object_id) {
PropertiesPanelState state;
state.entity = {"IfcWall", "SOLIDWALL"};
state.attributes = {
{"GlobalId", "2Q$n5SLPP9Q8B7wQKjKfUQ"},
{"Name", "Core-EXT-204"},
{"Description", "External load-bearing wall"},
};
state_.relationships = {
state.relationships = {
{"Type", "Basic Wall: Exterior - 200mm"},
{"Container", "Level 02"},
};
state_.property_sets = {
state.property_sets = {
{"Pset_WallCommon",
{{"Reference", "Core-EXT-204"},
{"Status", "Reviewed"},
@@ -53,7 +67,7 @@ PropertiesPanelView::PropertiesPanelView(PropertiesPanelWidget* widget, QObject*
{"Last Review", "2026-04-30"},
{"Assigned To", "Design Coordination"}}},
};
state_.quantity_sets = {
state.quantity_sets = {
{"BaseQuantities",
{{"Length", "6.20 m"},
{"Height", "3.45 m"},
@@ -65,7 +79,25 @@ PropertiesPanelView::PropertiesPanelView(PropertiesPanelWidget* widget, QObject*
{"Paint Coverage", "42.78 m2"}}},
};
widget_->setState(state_);
if (registry_) {
auto info = registry_->find(object_id);
if (info && !info->type.isEmpty()) {
state.entity.entity_class = info->type;
if (!state.property_sets.isEmpty() && !state.property_sets[1].rows.isEmpty()) {
state.property_sets[1].rows[0].value = info->type;
}
}
if (info && !info->name.isEmpty()) {
state.attributes[1].value = info->name;
if (state.property_sets.size() > 1 && state.property_sets[1].rows.size() > 1) {
state.property_sets[1].rows[1].value = info->name;
}
}
if (info && !info->guid.isEmpty()) {
state.attributes[0].value = info->guid;
}
}
widget_->render(state);
}
} // namespace ifcinterface::panels::properties
@@ -25,6 +25,8 @@
#include <QObject>
namespace ifcinterface { class ElementRegistry; }
class ViewportWindow;
namespace ifcinterface::panels::properties {
class PropertiesPanelWidget;
@@ -32,11 +34,16 @@ class PropertiesPanelWidget;
class PropertiesPanelView : public QObject {
Q_OBJECT
public:
explicit PropertiesPanelView(PropertiesPanelWidget* widget, QObject* parent = nullptr);
explicit PropertiesPanelView(PropertiesPanelWidget* widget,
ViewportWindow* viewport,
ifcinterface::ElementRegistry* registry,
QObject* parent = nullptr);
private:
void refresh(uint32_t object_id);
PropertiesPanelWidget* widget_ = nullptr;
PropertiesPanelState state_;
ifcinterface::ElementRegistry* registry_ = nullptr;
};
} // namespace ifcinterface::panels::properties
@@ -20,10 +20,10 @@
#include "PropertiesPanelWidget.h"
#include "../../components/KeyValueTable.h"
#include "../../components/Section.h"
#include "../../components/SvgIcon.h"
#include <QFormLayout>
#include <QFrame>
#include <QGroupBox>
#include <QHBoxLayout>
@@ -36,79 +36,37 @@ namespace {
QWidget* makePropertySetPanel(const ifcinterface::panels::properties::PropertySet& property_set, QWidget* parent = nullptr) {
auto* group = new QGroupBox(property_set.title, parent);
group->setObjectName("propertySetCard");
auto* form = new QFormLayout(group);
form->setContentsMargins(10, 10, 10, 10);
form->setHorizontalSpacing(16);
form->setVerticalSpacing(6);
form->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
auto* layout = new QVBoxLayout(group);
layout->setContentsMargins(10, 10, 10, 10);
layout->setSpacing(0);
QList<ifcinterface::components::KeyValueTableRow> rows;
for (const auto& row : property_set.rows) {
auto* key = new QLabel(row.key, group);
key->setObjectName("propertyKeyLabel");
auto* value = new QLabel(row.value, group);
value->setObjectName("propertyValueLabel");
value->setWordWrap(true);
form->addRow(key, value);
rows.append({row.key, row.value, "propertyValueLabel", "", "", 0});
}
layout->addWidget(new ifcinterface::components::KeyValueTable(rows, group));
return group;
}
QWidget* makeAttributeList(const QList<ifcinterface::panels::properties::KeyValueRow>& rows, QWidget* parent = nullptr) {
auto* panel = new QWidget(parent);
panel->setObjectName("attributeList");
auto* form = new QFormLayout(panel);
form->setContentsMargins(0, 0, 0, 0);
form->setHorizontalSpacing(16);
form->setVerticalSpacing(6);
form->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
QList<ifcinterface::components::KeyValueTableRow> table_rows;
for (const auto& row : rows) {
auto* key = new QLabel(row.key, panel);
key->setObjectName("propertyKeyLabel");
auto* value = new QLabel(row.value, panel);
value->setObjectName("propertyValueLabel");
value->setWordWrap(true);
form->addRow(key, value);
table_rows.append({row.key, row.value, "propertyValueLabel", "", "", 0});
}
return panel;
return new ifcinterface::components::KeyValueTable(table_rows, parent);
}
QWidget* makeRelationshipList(const QList<ifcinterface::panels::properties::RelationshipRow>& rows, QWidget* parent = nullptr) {
auto* panel = new QWidget(parent);
panel->setObjectName("attributeList");
auto* layout = new QVBoxLayout(panel);
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(6);
QList<ifcinterface::components::KeyValueTableRow> table_rows;
for (const auto& row_data : rows) {
auto* row = new QWidget(panel);
row->setObjectName("relationshipRow");
auto* row_layout = new QHBoxLayout(row);
row_layout->setContentsMargins(0, 0, 0, 0);
row_layout->setSpacing(12);
auto* key = new QLabel(row_data.key, row);
key->setObjectName("propertyKeyLabel");
key->setMinimumWidth(72);
auto* target = new QLabel(row_data.value, row);
target->setObjectName("relationshipValueLabel");
target->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
auto* icon = new QLabel(row);
icon->setObjectName("relationshipIconLabel");
icon->setPixmap(ifcinterface::components::icons::makePanelSvgPixmap(":/icons/cursor-pointer.svg", QSize(14, 14)));
icon->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
row_layout->addWidget(key);
row_layout->addWidget(target, 1);
row_layout->addWidget(icon, 0, Qt::AlignRight | Qt::AlignVCenter);
layout->addWidget(row);
table_rows.append({row_data.key,
row_data.value,
"relationshipValueLabel",
":/icons/cursor-pointer.svg",
"relationshipIconLabel",
72});
}
return panel;
return new ifcinterface::components::KeyValueTable(table_rows, parent);
}
} // namespace
@@ -121,22 +79,28 @@ PropertiesPanelWidget::PropertiesPanelWidget(QWidget* parent)
auto* root = new QVBoxLayout(this);
root->setContentsMargins(0, 0, 0, 0);
root->setSpacing(0);
scroll_ = new QScrollArea(this);
scroll_->setWidgetResizable(true);
scroll_->setFrameShape(QFrame::NoFrame);
content_ = new QWidget(scroll_);
content_->setObjectName("inspectorPanel");
content_layout_ = new QVBoxLayout(content_);
content_layout_->setContentsMargins(0, 0, 0, 0);
content_layout_->setSpacing(12);
scroll_->setWidget(content_);
root->addWidget(scroll_);
}
void PropertiesPanelWidget::setState(const PropertiesPanelState& state) {
auto* root = qobject_cast<QVBoxLayout*>(layout());
while (auto* item = root->takeAt(0)) {
void PropertiesPanelWidget::render(const PropertiesPanelState& state) {
while (auto* item = content_layout_->takeAt(0)) {
if (auto* widget = item->widget()) widget->deleteLater();
delete item;
}
auto* content = new QWidget(this);
content->setObjectName("inspectorPanel");
auto* content_layout = new QVBoxLayout(content);
content_layout->setContentsMargins(0, 0, 0, 0);
content_layout->setSpacing(12);
auto* entity_card = new QFrame(content);
auto* entity_card = new QFrame(content_);
entity_card->setObjectName("entityClassCard");
auto* entity_layout = new QHBoxLayout(entity_card);
entity_layout->setContentsMargins(10, 8, 10, 8);
@@ -159,37 +123,31 @@ void PropertiesPanelWidget::setState(const PropertiesPanelState& state) {
QList<QWidget*> property_set_widgets;
for (const auto& property_set : state.property_sets) {
property_set_widgets.append(makePropertySetPanel(property_set, content));
property_set_widgets.append(makePropertySetPanel(property_set, content_));
}
QList<QWidget*> quantity_set_widgets;
for (const auto& property_set : state.quantity_sets) {
quantity_set_widgets.append(makePropertySetPanel(property_set, content));
quantity_set_widgets.append(makePropertySetPanel(property_set, content_));
}
auto* entity_section = new components::Section("", components::SectionHeaderMode::Hidden, "", content);
auto* entity_section = new components::Section("", components::SectionHeaderMode::Hidden, "", content_);
entity_section->addBodyWidget(entity_card);
auto* attributes_section = new components::Section("Attributes", components::SectionHeaderMode::Visible, "", content);
attributes_section->addBodyWidget(makeAttributeList(state.attributes, content));
auto* relationships_section = new components::Section("Relationships", components::SectionHeaderMode::Visible, "", content);
relationships_section->addBodyWidget(makeRelationshipList(state.relationships, content));
auto* properties_section = new components::Section("Properties", components::SectionHeaderMode::Visible, "Filter properties or sets", content);
auto* attributes_section = new components::Section("Attributes", components::SectionHeaderMode::Visible, "", content_);
attributes_section->addBodyWidget(makeAttributeList(state.attributes, content_));
auto* relationships_section = new components::Section("Relationships", components::SectionHeaderMode::Visible, "", content_);
relationships_section->addBodyWidget(makeRelationshipList(state.relationships, content_));
auto* properties_section = new components::Section("Properties", components::SectionHeaderMode::Visible, "Filter properties or sets", content_);
for (auto* widget : property_set_widgets) properties_section->addBodyWidget(widget);
auto* quantities_section = new components::Section("Quantities", components::SectionHeaderMode::Visible, "Filter quantities or sets", content);
auto* quantities_section = new components::Section("Quantities", components::SectionHeaderMode::Visible, "Filter quantities or sets", content_);
for (auto* widget : quantity_set_widgets) quantities_section->addBodyWidget(widget);
content_layout->addWidget(entity_section);
content_layout->addWidget(attributes_section);
content_layout->addWidget(relationships_section);
content_layout->addWidget(properties_section);
content_layout->addWidget(quantities_section);
content_layout->addStretch(1);
auto* scroll = new QScrollArea(this);
scroll->setWidgetResizable(true);
scroll->setFrameShape(QFrame::NoFrame);
scroll->setWidget(content);
root->addWidget(scroll);
content_layout_->addWidget(entity_section);
content_layout_->addWidget(attributes_section);
content_layout_->addWidget(relationships_section);
content_layout_->addWidget(properties_section);
content_layout_->addWidget(quantities_section);
content_layout_->addStretch(1);
}
} // namespace ifcinterface::panels::properties
@@ -25,6 +25,9 @@
#include <QWidget>
class QScrollArea;
class QVBoxLayout;
namespace ifcinterface::panels::properties {
class PropertiesPanelWidget : public QWidget {
@@ -32,7 +35,12 @@ class PropertiesPanelWidget : public QWidget {
public:
explicit PropertiesPanelWidget(QWidget* parent = nullptr);
void setState(const PropertiesPanelState& state);
void render(const PropertiesPanelState& state);
private:
QScrollArea* scroll_ = nullptr;
QWidget* content_ = nullptr;
QVBoxLayout* content_layout_ = nullptr;
};
} // namespace ifcinterface::panels::properties