mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-13 06:53:36 +00:00
Interface mockup 2
This commit is contained in:
@@ -27,6 +27,21 @@ set(INTERFACE_FILES
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/MockMainWindow.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/MockMainWindow.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/MockMainWindow.h
|
${CMAKE_CURRENT_SOURCE_DIR}/MockMainWindow.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
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/panels/models/ModelsPanelView.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/panels/models/ModelsPanelView.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/panels/properties/PropertiesPanelTypes.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/panels/properties/PropertiesPanelWidget.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/panels/properties/PropertiesPanelWidget.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/panels/properties/PropertiesPanelView.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/panels/properties/PropertiesPanelView.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/panels/spatial_hierarchy/SpatialHierarchyPanelTypes.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/panels/spatial_hierarchy/SpatialHierarchyPanelWidget.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/panels/spatial_hierarchy/SpatialHierarchyPanelWidget.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/panels/spatial_hierarchy/SpatialHierarchyPanelView.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/panels/spatial_hierarchy/SpatialHierarchyPanelView.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/interface_resources.qrc
|
${CMAKE_CURRENT_SOURCE_DIR}/interface_resources.qrc
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,12 @@
|
|||||||
#include "AppSettings.h"
|
#include "AppSettings.h"
|
||||||
#include "SceneLoader.h"
|
#include "SceneLoader.h"
|
||||||
#include "ViewportWindow.h"
|
#include "ViewportWindow.h"
|
||||||
|
#include "panels/models/ModelsPanelView.h"
|
||||||
|
#include "panels/models/ModelsPanelWidget.h"
|
||||||
|
#include "panels/properties/PropertiesPanelView.h"
|
||||||
|
#include "panels/properties/PropertiesPanelWidget.h"
|
||||||
|
#include "panels/spatial_hierarchy/SpatialHierarchyPanelView.h"
|
||||||
|
#include "panels/spatial_hierarchy/SpatialHierarchyPanelWidget.h"
|
||||||
|
|
||||||
#include <QDockWidget>
|
#include <QDockWidget>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
@@ -93,10 +99,6 @@ QIcon makePanelSvgIcon(const QString& icon_path) {
|
|||||||
return makeTintedSvgIcon(icon_path, "#e7ebf2", "#ffffff", "#6f7988");
|
return makeTintedSvgIcon(icon_path, "#e7ebf2", "#ffffff", "#6f7988");
|
||||||
}
|
}
|
||||||
|
|
||||||
QPixmap makePanelSvgPixmap(const QString& icon_path, const QSize& size) {
|
|
||||||
return renderTintedSvgPixmap(icon_path, "#e7ebf2", size);
|
|
||||||
}
|
|
||||||
|
|
||||||
class DockTitleBar : public QWidget {
|
class DockTitleBar : public QWidget {
|
||||||
public:
|
public:
|
||||||
explicit DockTitleBar(const QString& title, bool has_settings = false, QWidget* parent = nullptr)
|
explicit DockTitleBar(const QString& title, bool has_settings = false, QWidget* parent = nullptr)
|
||||||
@@ -175,165 +177,6 @@ QFrame* wrapInspectorPanel(QWidget* inner) {
|
|||||||
return outer;
|
return outer;
|
||||||
}
|
}
|
||||||
|
|
||||||
QWidget* makeInspectorFilterField(const QString& placeholder, QWidget* parent = nullptr) {
|
|
||||||
auto* field = new QLineEdit(parent);
|
|
||||||
field->setPlaceholderText(placeholder);
|
|
||||||
field->setClearButtonEnabled(true);
|
|
||||||
field->addAction(makePanelSvgIcon(":/icons/filter.svg"), QLineEdit::LeadingPosition);
|
|
||||||
return field;
|
|
||||||
}
|
|
||||||
|
|
||||||
QWidget* makePropertySetPanel(const QString& title,
|
|
||||||
const QList<QPair<QString, QString>>& rows,
|
|
||||||
QWidget* parent = nullptr) {
|
|
||||||
auto* group = new QGroupBox(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);
|
|
||||||
|
|
||||||
for (const auto& [name, value] : rows) {
|
|
||||||
auto* key = new QLabel(name, group);
|
|
||||||
key->setObjectName("propertyKeyLabel");
|
|
||||||
auto* val = new QLabel(value, group);
|
|
||||||
val->setObjectName("propertyValueLabel");
|
|
||||||
val->setWordWrap(true);
|
|
||||||
form->addRow(key, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
return group;
|
|
||||||
}
|
|
||||||
|
|
||||||
QWidget* makeInspectorSection(const QString& title,
|
|
||||||
const QString& filter_placeholder,
|
|
||||||
const QList<QWidget*>& groups,
|
|
||||||
QWidget* parent = nullptr) {
|
|
||||||
auto* section = new QWidget(parent);
|
|
||||||
section->setObjectName("inspectorSection");
|
|
||||||
auto* layout = new QVBoxLayout(section);
|
|
||||||
layout->setContentsMargins(0, 0, 0, 0);
|
|
||||||
layout->setSpacing(6);
|
|
||||||
|
|
||||||
auto* header = new QFrame(section);
|
|
||||||
header->setObjectName("inspectorSectionHeader");
|
|
||||||
auto* header_layout = new QHBoxLayout(header);
|
|
||||||
header_layout->setContentsMargins(0, 0, 0, 0);
|
|
||||||
header_layout->setSpacing(6);
|
|
||||||
|
|
||||||
auto* toggle = new QToolButton(header);
|
|
||||||
toggle->setObjectName("inspectorSectionButton");
|
|
||||||
toggle->setText(title);
|
|
||||||
toggle->setCheckable(true);
|
|
||||||
toggle->setChecked(true);
|
|
||||||
toggle->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
|
||||||
toggle->setArrowType(Qt::DownArrow);
|
|
||||||
header_layout->addWidget(toggle);
|
|
||||||
header_layout->addStretch(1);
|
|
||||||
|
|
||||||
QLineEdit* filter_field = nullptr;
|
|
||||||
if (!filter_placeholder.isEmpty()) {
|
|
||||||
auto* filter_toggle = new QToolButton(header);
|
|
||||||
filter_toggle->setObjectName("inspectorFilterToggle");
|
|
||||||
filter_toggle->setCheckable(true);
|
|
||||||
filter_toggle->setChecked(false);
|
|
||||||
filter_toggle->setIcon(makePanelSvgIcon(":/icons/filter.svg"));
|
|
||||||
filter_toggle->setAutoRaise(true);
|
|
||||||
filter_toggle->setToolTip(QString("Filter %1").arg(title.toLower()));
|
|
||||||
header_layout->addWidget(filter_toggle);
|
|
||||||
|
|
||||||
filter_field = qobject_cast<QLineEdit*>(makeInspectorFilterField(filter_placeholder, section));
|
|
||||||
filter_field->setVisible(false);
|
|
||||||
QObject::connect(filter_toggle, &QToolButton::toggled, filter_field, [filter_field](bool visible) {
|
|
||||||
filter_field->setVisible(visible);
|
|
||||||
if (visible) filter_field->setFocus();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* body = new QWidget(section);
|
|
||||||
body->setObjectName("inspectorSectionBody");
|
|
||||||
auto* body_layout = new QVBoxLayout(body);
|
|
||||||
body_layout->setContentsMargins(10, 6, 10, 0);
|
|
||||||
body_layout->setSpacing(6);
|
|
||||||
for (auto* group : groups) body_layout->addWidget(group);
|
|
||||||
|
|
||||||
QObject::connect(toggle, &QToolButton::toggled, body, [toggle, body](bool expanded) {
|
|
||||||
toggle->setArrowType(expanded ? Qt::DownArrow : Qt::RightArrow);
|
|
||||||
body->setVisible(expanded);
|
|
||||||
});
|
|
||||||
|
|
||||||
layout->addWidget(header);
|
|
||||||
if (filter_field) {
|
|
||||||
auto* filter_wrapper = new QWidget(section);
|
|
||||||
filter_wrapper->setObjectName("inspectorFilterWrapper");
|
|
||||||
auto* filter_wrapper_layout = new QVBoxLayout(filter_wrapper);
|
|
||||||
filter_wrapper_layout->setContentsMargins(10, 0, 10, 0);
|
|
||||||
filter_wrapper_layout->setSpacing(0);
|
|
||||||
filter_wrapper_layout->addWidget(filter_field);
|
|
||||||
layout->addWidget(filter_wrapper);
|
|
||||||
}
|
|
||||||
layout->addWidget(body);
|
|
||||||
return section;
|
|
||||||
}
|
|
||||||
|
|
||||||
QWidget* makeAttributeList(const QList<QPair<QString, QString>>& 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);
|
|
||||||
|
|
||||||
for (const auto& [name, value] : rows) {
|
|
||||||
auto* key = new QLabel(name, panel);
|
|
||||||
key->setObjectName("propertyKeyLabel");
|
|
||||||
auto* val = new QLabel(value, panel);
|
|
||||||
val->setObjectName("propertyValueLabel");
|
|
||||||
val->setWordWrap(true);
|
|
||||||
form->addRow(key, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
return panel;
|
|
||||||
}
|
|
||||||
|
|
||||||
QWidget* makeRelationshipList(const QList<QPair<QString, QString>>& 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);
|
|
||||||
|
|
||||||
for (const auto& [name, value] : 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(name, row);
|
|
||||||
key->setObjectName("propertyKeyLabel");
|
|
||||||
key->setMinimumWidth(72);
|
|
||||||
|
|
||||||
auto* target = new QLabel(value, row);
|
|
||||||
target->setObjectName("relationshipValueLabel");
|
|
||||||
target->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
|
||||||
|
|
||||||
auto* icon = new QLabel(row);
|
|
||||||
icon->setObjectName("relationshipIconLabel");
|
|
||||||
icon->setPixmap(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);
|
|
||||||
}
|
|
||||||
|
|
||||||
return panel;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
MockMainWindow::MockMainWindow(QWidget* parent)
|
MockMainWindow::MockMainWindow(QWidget* parent)
|
||||||
@@ -931,231 +774,22 @@ void MockMainWindow::setupViewport() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MockMainWindow::setupDocks() {
|
void MockMainWindow::setupDocks() {
|
||||||
auto* models_panel = new QWidget(this);
|
auto* models_panel = new ifcinterface::panels::models::ModelsPanelWidget(this);
|
||||||
auto* models_panel_layout = new QVBoxLayout(models_panel);
|
auto* spatial_panel = new ifcinterface::panels::spatial_hierarchy::SpatialHierarchyPanelWidget(this);
|
||||||
models_panel_layout->setContentsMargins(0, 0, 0, 0);
|
auto* properties_panel = new ifcinterface::panels::properties::PropertiesPanelWidget(this);
|
||||||
models_panel_layout->setSpacing(0);
|
|
||||||
|
|
||||||
auto* models_tree = new QTreeWidget(this);
|
models_panel_view_ = new ifcinterface::panels::models::ModelsPanelView(models_panel, this);
|
||||||
models_tree->setColumnCount(2);
|
spatial_panel_view_ = new ifcinterface::panels::spatial_hierarchy::SpatialHierarchyPanelView(spatial_panel, this);
|
||||||
models_tree->setHeaderLabels({"Model", ""});
|
properties_panel_view_ = new ifcinterface::panels::properties::PropertiesPanelView(properties_panel, this);
|
||||||
models_tree->setIconSize(QSize(16, 16));
|
|
||||||
models_tree->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
|
||||||
models_tree->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
||||||
models_tree->setUniformRowHeights(true);
|
|
||||||
models_tree->header()->setStretchLastSection(false);
|
|
||||||
models_tree->header()->setSectionResizeMode(0, QHeaderView::Stretch);
|
|
||||||
models_tree->header()->setSectionResizeMode(1, QHeaderView::Fixed);
|
|
||||||
models_tree->header()->resizeSection(1, 28);
|
|
||||||
models_tree->header()->hide();
|
|
||||||
|
|
||||||
auto* local_group = new QTreeWidgetItem(models_tree, {"Local Models", ""});
|
connect(models_panel_view_, &ifcinterface::panels::models::ModelsPanelView::statusMessageRequested,
|
||||||
local_group->setIcon(0, makePanelSvgIcon(":/icons/folder.svg"));
|
this, &MockMainWindow::setStatusMessage);
|
||||||
local_group->setIcon(1, makePanelSvgIcon(":/icons/eye.svg"));
|
connect(spatial_panel_view_, &ifcinterface::panels::spatial_hierarchy::SpatialHierarchyPanelView::statusMessageRequested,
|
||||||
local_group->setData(1, Qt::UserRole, true);
|
this, &MockMainWindow::setStatusMessage);
|
||||||
local_group->setSizeHint(0, QSize(0, 24));
|
|
||||||
auto* linked_group = new QTreeWidgetItem(models_tree, {"Linked Models", ""});
|
|
||||||
linked_group->setIcon(0, makePanelSvgIcon(":/icons/folder.svg"));
|
|
||||||
linked_group->setIcon(1, makePanelSvgIcon(":/icons/eye.svg"));
|
|
||||||
linked_group->setData(1, Qt::UserRole, true);
|
|
||||||
linked_group->setSizeHint(0, QSize(0, 24));
|
|
||||||
|
|
||||||
auto make_model_item = [this](QTreeWidgetItem* parent, const QString& name, bool visible) {
|
|
||||||
auto* item = new QTreeWidgetItem(parent, {name, ""});
|
|
||||||
item->setIcon(0, makePanelSvgIcon(":/icons/cube.svg"));
|
|
||||||
item->setIcon(1, makePanelSvgIcon(visible ? ":/icons/eye-solid.svg" : ":/icons/eye.svg"));
|
|
||||||
item->setData(1, Qt::UserRole, visible);
|
|
||||||
item->setSizeHint(0, QSize(0, 24));
|
|
||||||
return item;
|
|
||||||
};
|
|
||||||
|
|
||||||
make_model_item(local_group, "Architecture.ifc", true);
|
|
||||||
make_model_item(local_group, "Structure.ifc", true);
|
|
||||||
make_model_item(linked_group, "MEP.ifc", false);
|
|
||||||
models_tree->expandAll();
|
|
||||||
|
|
||||||
models_panel_layout->addWidget(models_tree);
|
|
||||||
|
|
||||||
connect(models_tree, &QTreeWidget::itemClicked, this, [this](QTreeWidgetItem* item, int column) {
|
|
||||||
if (!item || column != 1) return;
|
|
||||||
const bool visible = item->data(1, Qt::UserRole).toBool();
|
|
||||||
const bool next_visible = !visible;
|
|
||||||
item->setData(1, Qt::UserRole, next_visible);
|
|
||||||
if (item->childCount() > 0) {
|
|
||||||
item->setIcon(1, makePanelSvgIcon(next_visible ? ":/icons/eye.svg" : ":/icons/eye-closed.svg"));
|
|
||||||
} else {
|
|
||||||
item->setIcon(1, makePanelSvgIcon(next_visible ? ":/icons/eye-solid.svg" : ":/icons/eye-closed.svg"));
|
|
||||||
}
|
|
||||||
setStatusMessage("Models", next_visible ? "Item shown" : "Item hidden");
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(models_tree, &QTreeWidget::customContextMenuRequested, this, [this, models_tree](const QPoint& pos) {
|
|
||||||
auto* item = models_tree->itemAt(pos);
|
|
||||||
QMenu menu(models_tree);
|
|
||||||
if (item && item->childCount() > 0) {
|
|
||||||
menu.addAction(makePanelSvgIcon(":/icons/folder-plus.svg"), "Add Group", [this]() {
|
|
||||||
setStatusMessage("Models", "Add Group coming soon");
|
|
||||||
});
|
|
||||||
menu.addAction(makePanelSvgIcon(":/icons/folder-minus.svg"), "Remove Group", [this]() {
|
|
||||||
setStatusMessage("Models", "Remove Group coming soon");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (item && item->childCount() == 0) {
|
|
||||||
menu.addAction(makePanelSvgIcon(":/icons/minus-square.svg"), "Remove Model", [this]() {
|
|
||||||
setStatusMessage("Models", "Remove Model coming soon");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
menu.addAction(makePanelSvgIcon(":/icons/intersect.svg"), "Invert Visibility", [this]() {
|
|
||||||
setStatusMessage("Models", "Invert visibility coming soon");
|
|
||||||
});
|
|
||||||
if (!menu.actions().isEmpty()) menu.exec(models_tree->viewport()->mapToGlobal(pos));
|
|
||||||
});
|
|
||||||
|
|
||||||
auto* spatial_tree = new QTreeWidget(this);
|
|
||||||
spatial_tree->setColumnCount(2);
|
|
||||||
spatial_tree->setHeaderLabels({"Spatial Item", ""});
|
|
||||||
spatial_tree->setIconSize(QSize(16, 16));
|
|
||||||
spatial_tree->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
|
||||||
spatial_tree->setUniformRowHeights(true);
|
|
||||||
spatial_tree->header()->setStretchLastSection(false);
|
|
||||||
spatial_tree->header()->setSectionResizeMode(0, QHeaderView::Stretch);
|
|
||||||
spatial_tree->header()->setSectionResizeMode(1, QHeaderView::Fixed);
|
|
||||||
spatial_tree->header()->resizeSection(1, 28);
|
|
||||||
spatial_tree->header()->hide();
|
|
||||||
|
|
||||||
auto make_spatial_item = [this](QTreeWidgetItem* parent, const QString& name, const QString& icon_path) {
|
|
||||||
auto* item = new QTreeWidgetItem(parent, {name, ""});
|
|
||||||
item->setIcon(0, makePanelSvgIcon(icon_path));
|
|
||||||
item->setIcon(1, makePanelSvgIcon(":/icons/eye.svg"));
|
|
||||||
item->setData(1, Qt::UserRole, true);
|
|
||||||
item->setSizeHint(0, QSize(0, 24));
|
|
||||||
return item;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto* site = make_spatial_item(spatial_tree->invisibleRootItem(), "Site A", ":/icons/frame-alt.svg");
|
|
||||||
auto* building = make_spatial_item(site, "Building 01", ":/icons/city.svg");
|
|
||||||
auto* storey = make_spatial_item(building, "Level 02", ":/icons/planimetry.svg");
|
|
||||||
make_spatial_item(storey, "Lobby", ":/icons/square3d-from-center.svg");
|
|
||||||
make_spatial_item(storey, "Core", ":/icons/square3d-from-center.svg");
|
|
||||||
spatial_tree->expandAll();
|
|
||||||
|
|
||||||
connect(spatial_tree, &QTreeWidget::itemClicked, this, [this](QTreeWidgetItem* item, int column) {
|
|
||||||
if (!item || column != 1) return;
|
|
||||||
const bool visible = item->data(1, Qt::UserRole).toBool();
|
|
||||||
const bool next_visible = !visible;
|
|
||||||
item->setData(1, Qt::UserRole, next_visible);
|
|
||||||
item->setIcon(1, makePanelSvgIcon(next_visible ? ":/icons/eye.svg" : ":/icons/eye-closed.svg"));
|
|
||||||
setStatusMessage("Spatial", next_visible ? "Item shown" : "Item hidden");
|
|
||||||
});
|
|
||||||
|
|
||||||
auto* properties_content = new QWidget(this);
|
|
||||||
properties_content->setObjectName("inspectorPanel");
|
|
||||||
auto* properties_layout = new QVBoxLayout(properties_content);
|
|
||||||
properties_layout->setContentsMargins(0, 0, 0, 0);
|
|
||||||
properties_layout->setSpacing(12);
|
|
||||||
|
|
||||||
auto* entity_card = new QFrame(properties_content);
|
|
||||||
entity_card->setObjectName("entityClassCard");
|
|
||||||
auto* entity_layout = new QHBoxLayout(entity_card);
|
|
||||||
entity_layout->setContentsMargins(10, 8, 10, 8);
|
|
||||||
entity_layout->setSpacing(10);
|
|
||||||
auto* entity_icon = new QLabel(entity_card);
|
|
||||||
entity_icon->setPixmap(makePanelSvgPixmap(":/icons/cube-dots.svg", QSize(28, 28)));
|
|
||||||
entity_icon->setAlignment(Qt::AlignCenter);
|
|
||||||
auto* entity_text = new QWidget(entity_card);
|
|
||||||
auto* entity_text_layout = new QVBoxLayout(entity_text);
|
|
||||||
entity_text_layout->setContentsMargins(0, 0, 0, 0);
|
|
||||||
entity_text_layout->setSpacing(2);
|
|
||||||
auto* entity_class = new QLabel("IfcWall", entity_text);
|
|
||||||
entity_class->setObjectName("entityClassLabel");
|
|
||||||
auto* entity_type = new QLabel("SOLIDWALL", entity_text);
|
|
||||||
entity_type->setObjectName("entityTypeLabel");
|
|
||||||
entity_text_layout->addWidget(entity_class);
|
|
||||||
entity_text_layout->addWidget(entity_type);
|
|
||||||
entity_layout->addWidget(entity_icon, 0, Qt::AlignVCenter);
|
|
||||||
entity_layout->addWidget(entity_text, 1, Qt::AlignVCenter);
|
|
||||||
|
|
||||||
auto* attributes_section = makeInspectorSection(
|
|
||||||
"Attributes", "",
|
|
||||||
{
|
|
||||||
makeAttributeList({{"GlobalId", "2Q$n5SLPP9Q8B7wQKjKfUQ"},
|
|
||||||
{"Name", "Core-EXT-204"},
|
|
||||||
{"Description", "External load-bearing wall"}}, properties_content)
|
|
||||||
},
|
|
||||||
properties_content);
|
|
||||||
|
|
||||||
auto* properties_section = makeInspectorSection(
|
|
||||||
"Properties", "Filter properties or sets",
|
|
||||||
{
|
|
||||||
makePropertySetPanel("Pset_WallCommon",
|
|
||||||
{{"Reference", "Core-EXT-204"},
|
|
||||||
{"Status", "Reviewed"},
|
|
||||||
{"Fire Rating", "120 min"},
|
|
||||||
{"LoadBearing", "True"}},
|
|
||||||
properties_content),
|
|
||||||
makePropertySetPanel("Identity Data",
|
|
||||||
{{"Type", "IfcWall"},
|
|
||||||
{"Name", "Core-EXT-204"},
|
|
||||||
{"Owner", "Architecture"},
|
|
||||||
{"Phase", "Construction"}},
|
|
||||||
properties_content),
|
|
||||||
makePropertySetPanel("BIM Collaboration",
|
|
||||||
{{"Issue Count", "2 open"},
|
|
||||||
{"Last Review", "2026-04-30"},
|
|
||||||
{"Assigned To", "Design Coordination"}},
|
|
||||||
properties_content)
|
|
||||||
},
|
|
||||||
properties_content);
|
|
||||||
|
|
||||||
qobject_cast<QVBoxLayout*>(attributes_section->layout())->setSpacing(8);
|
|
||||||
|
|
||||||
auto* relationships_section = makeInspectorSection(
|
|
||||||
"Relationships", "",
|
|
||||||
{
|
|
||||||
makeRelationshipList({{"Type", "Basic Wall: Exterior - 200mm"},
|
|
||||||
{"Container", "Level 02"}}, properties_content)
|
|
||||||
},
|
|
||||||
properties_content);
|
|
||||||
|
|
||||||
auto* quantities_section = makeInspectorSection(
|
|
||||||
"Quantities", "Filter quantities or sets",
|
|
||||||
{
|
|
||||||
makePropertySetPanel("BaseQuantities",
|
|
||||||
{{"Length", "6.20 m"},
|
|
||||||
{"Height", "3.45 m"},
|
|
||||||
{"Width", "0.30 m"},
|
|
||||||
{"Volume", "6.42 m3"}},
|
|
||||||
properties_content),
|
|
||||||
makePropertySetPanel("Finish Quantities",
|
|
||||||
{{"NetSideArea", "21.39 m2"},
|
|
||||||
{"GrossArea", "22.10 m2"},
|
|
||||||
{"Paint Coverage", "42.78 m2"}},
|
|
||||||
properties_content)
|
|
||||||
},
|
|
||||||
properties_content);
|
|
||||||
|
|
||||||
auto* entity_wrapper = new QWidget(properties_content);
|
|
||||||
entity_wrapper->setObjectName("inspectorSectionBody");
|
|
||||||
auto* entity_wrapper_layout = new QVBoxLayout(entity_wrapper);
|
|
||||||
entity_wrapper_layout->setContentsMargins(10, 0, 10, 0);
|
|
||||||
entity_wrapper_layout->setSpacing(0);
|
|
||||||
entity_wrapper_layout->addWidget(entity_card);
|
|
||||||
|
|
||||||
properties_layout->addWidget(entity_wrapper);
|
|
||||||
properties_layout->addWidget(attributes_section);
|
|
||||||
properties_layout->addWidget(relationships_section);
|
|
||||||
properties_layout->addWidget(properties_section);
|
|
||||||
properties_layout->addWidget(quantities_section);
|
|
||||||
properties_layout->addStretch(1);
|
|
||||||
|
|
||||||
auto* properties_scroll = new QScrollArea(this);
|
|
||||||
properties_scroll->setWidgetResizable(true);
|
|
||||||
properties_scroll->setFrameShape(QFrame::NoFrame);
|
|
||||||
properties_scroll->setWidget(properties_content);
|
|
||||||
|
|
||||||
models_dock_ = makeDock("Models", wrapPanel(models_panel), this, true);
|
models_dock_ = makeDock("Models", wrapPanel(models_panel), this, true);
|
||||||
spatial_dock_ = makeDock("Spatial Hierarchy", wrapPanel(spatial_tree), this);
|
spatial_dock_ = makeDock("Spatial Hierarchy", wrapPanel(spatial_panel), this);
|
||||||
properties_dock_ = makeDock("Properties", wrapInspectorPanel(properties_scroll), this);
|
properties_dock_ = makeDock("Properties", wrapInspectorPanel(properties_panel), this);
|
||||||
layers_dock_ = makeDock("Layers", wrapPanel(makeComingSoonPanel("Layers")), this);
|
layers_dock_ = makeDock("Layers", wrapPanel(makeComingSoonPanel("Layers")), this);
|
||||||
stored_views_dock_ = makeDock("Stored Views", wrapPanel(makeComingSoonPanel("Stored Views")), this);
|
stored_views_dock_ = makeDock("Stored Views", wrapPanel(makeComingSoonPanel("Stored Views")), this);
|
||||||
search_dock_ = makeDock("Search and Query", wrapPanel(makeComingSoonPanel("Search and Query")), this);
|
search_dock_ = makeDock("Search and Query", wrapPanel(makeComingSoonPanel("Search and Query")), this);
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ class QTabBar;
|
|||||||
class QToolButton;
|
class QToolButton;
|
||||||
class ViewportWindow;
|
class ViewportWindow;
|
||||||
class SceneLoader;
|
class SceneLoader;
|
||||||
|
namespace ifcinterface::panels::models { class ModelsPanelView; }
|
||||||
|
namespace ifcinterface::panels::spatial_hierarchy { class SpatialHierarchyPanelView; }
|
||||||
|
namespace ifcinterface::panels::properties { class PropertiesPanelView; }
|
||||||
|
|
||||||
class MockMainWindow : public QMainWindow {
|
class MockMainWindow : public QMainWindow {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -83,6 +86,9 @@ private:
|
|||||||
QDockWidget* spreadsheet_dock_ = nullptr;
|
QDockWidget* spreadsheet_dock_ = nullptr;
|
||||||
QDockWidget* clash_dock_ = nullptr;
|
QDockWidget* clash_dock_ = nullptr;
|
||||||
QDockWidget* issues_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;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
// 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_PROPERTIESPANELTYPES_H
|
||||||
|
#define IFCINTERFACE_PANELS_PROPERTIESPANELTYPES_H
|
||||||
|
|
||||||
|
#include <QList>
|
||||||
|
#include <QPair>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace ifcinterface::panels::properties {
|
||||||
|
|
||||||
|
struct KeyValueRow {
|
||||||
|
QString key;
|
||||||
|
QString value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RelationshipRow {
|
||||||
|
QString key;
|
||||||
|
QString value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PropertySet {
|
||||||
|
QString title;
|
||||||
|
QList<KeyValueRow> rows;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EntitySummary {
|
||||||
|
QString entity_class;
|
||||||
|
QString predefined_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PropertiesPanelState {
|
||||||
|
EntitySummary entity;
|
||||||
|
QList<KeyValueRow> attributes;
|
||||||
|
QList<RelationshipRow> relationships;
|
||||||
|
QList<PropertySet> property_sets;
|
||||||
|
QList<PropertySet> quantity_sets;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ifcinterface::panels::properties
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -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/>. *
|
||||||
|
* *
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
#include "PropertiesPanelView.h"
|
||||||
|
|
||||||
|
#include "PropertiesPanelWidget.h"
|
||||||
|
|
||||||
|
namespace ifcinterface::panels::properties {
|
||||||
|
|
||||||
|
PropertiesPanelView::PropertiesPanelView(PropertiesPanelWidget* widget, QObject* parent)
|
||||||
|
: QObject(parent), widget_(widget)
|
||||||
|
{
|
||||||
|
state_.entity = {"IfcWall", "SOLIDWALL"};
|
||||||
|
state_.attributes = {
|
||||||
|
{"GlobalId", "2Q$n5SLPP9Q8B7wQKjKfUQ"},
|
||||||
|
{"Name", "Core-EXT-204"},
|
||||||
|
{"Description", "External load-bearing wall"},
|
||||||
|
};
|
||||||
|
state_.relationships = {
|
||||||
|
{"Type", "Basic Wall: Exterior - 200mm"},
|
||||||
|
{"Container", "Level 02"},
|
||||||
|
};
|
||||||
|
state_.property_sets = {
|
||||||
|
{"Pset_WallCommon",
|
||||||
|
{{"Reference", "Core-EXT-204"},
|
||||||
|
{"Status", "Reviewed"},
|
||||||
|
{"Fire Rating", "120 min"},
|
||||||
|
{"LoadBearing", "True"}}},
|
||||||
|
{"Identity Data",
|
||||||
|
{{"Type", "IfcWall"},
|
||||||
|
{"Name", "Core-EXT-204"},
|
||||||
|
{"Owner", "Architecture"},
|
||||||
|
{"Phase", "Construction"}}},
|
||||||
|
{"BIM Collaboration",
|
||||||
|
{{"Issue Count", "2 open"},
|
||||||
|
{"Last Review", "2026-04-30"},
|
||||||
|
{"Assigned To", "Design Coordination"}}},
|
||||||
|
};
|
||||||
|
state_.quantity_sets = {
|
||||||
|
{"BaseQuantities",
|
||||||
|
{{"Length", "6.20 m"},
|
||||||
|
{"Height", "3.45 m"},
|
||||||
|
{"Width", "0.30 m"},
|
||||||
|
{"Volume", "6.42 m3"}}},
|
||||||
|
{"Finish Quantities",
|
||||||
|
{{"NetSideArea", "21.39 m2"},
|
||||||
|
{"GrossArea", "22.10 m2"},
|
||||||
|
{"Paint Coverage", "42.78 m2"}}},
|
||||||
|
};
|
||||||
|
|
||||||
|
widget_->setState(state_);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ifcinterface::panels::properties
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
// 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_PROPERTIESPANELVIEW_H
|
||||||
|
#define IFCINTERFACE_PANELS_PROPERTIESPANELVIEW_H
|
||||||
|
|
||||||
|
#include "PropertiesPanelTypes.h"
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
namespace ifcinterface::panels::properties {
|
||||||
|
|
||||||
|
class PropertiesPanelWidget;
|
||||||
|
|
||||||
|
class PropertiesPanelView : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit PropertiesPanelView(PropertiesPanelWidget* widget, QObject* parent = nullptr);
|
||||||
|
|
||||||
|
private:
|
||||||
|
PropertiesPanelWidget* widget_ = nullptr;
|
||||||
|
PropertiesPanelState state_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ifcinterface::panels::properties
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,313 @@
|
|||||||
|
// 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 "PropertiesPanelWidget.h"
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
#include <QFormLayout>
|
||||||
|
#include <QFrame>
|
||||||
|
#include <QGroupBox>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QPixmap>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QScrollArea>
|
||||||
|
#include <QSvgRenderer>
|
||||||
|
#include <QToolButton>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
QPixmap renderPanelSvgPixmap(const QString& icon_path, const QString& color, const QSize& size) {
|
||||||
|
QFile file(icon_path);
|
||||||
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
|
return QIcon(icon_path).pixmap(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString svg = QString::fromUtf8(file.readAll());
|
||||||
|
svg.replace("currentColor", color, Qt::CaseSensitive);
|
||||||
|
svg.replace(QRegularExpression(R"(stroke="[^"]*")"), QString("stroke=\"%1\"").arg(color));
|
||||||
|
svg.replace(QRegularExpression(R"(fill="none")"), "fill=\"none\"");
|
||||||
|
|
||||||
|
QSvgRenderer renderer(svg.toUtf8());
|
||||||
|
QPixmap pixmap(size);
|
||||||
|
pixmap.fill(Qt::transparent);
|
||||||
|
QPainter painter(&pixmap);
|
||||||
|
renderer.render(&painter);
|
||||||
|
return pixmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
QIcon makePanelSvgIcon(const QString& icon_path) {
|
||||||
|
QIcon icon;
|
||||||
|
icon.addPixmap(renderPanelSvgPixmap(icon_path, "#e7ebf2", QSize(20, 20)), QIcon::Normal, QIcon::Off);
|
||||||
|
icon.addPixmap(renderPanelSvgPixmap(icon_path, "#ffffff", QSize(20, 20)), QIcon::Active, QIcon::Off);
|
||||||
|
icon.addPixmap(renderPanelSvgPixmap(icon_path, "#ffffff", QSize(20, 20)), QIcon::Selected, QIcon::Off);
|
||||||
|
icon.addPixmap(renderPanelSvgPixmap(icon_path, "#6f7988", QSize(20, 20)), QIcon::Disabled, QIcon::Off);
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap makePanelSvgPixmap(const QString& icon_path, const QSize& size) {
|
||||||
|
return renderPanelSvgPixmap(icon_path, "#e7ebf2", size);
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget* makeInspectorFilterField(const QString& placeholder, QWidget* parent = nullptr) {
|
||||||
|
auto* field = new QLineEdit(parent);
|
||||||
|
field->setPlaceholderText(placeholder);
|
||||||
|
field->setClearButtonEnabled(true);
|
||||||
|
field->addAction(makePanelSvgIcon(":/icons/filter.svg"), QLineEdit::LeadingPosition);
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget* makeInspectorSection(const QString& title,
|
||||||
|
const QString& filter_placeholder,
|
||||||
|
const QList<QWidget*>& groups,
|
||||||
|
QWidget* parent = nullptr) {
|
||||||
|
auto* section = new QWidget(parent);
|
||||||
|
section->setObjectName("inspectorSection");
|
||||||
|
auto* layout = new QVBoxLayout(section);
|
||||||
|
layout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
layout->setSpacing(6);
|
||||||
|
|
||||||
|
auto* header = new QFrame(section);
|
||||||
|
header->setObjectName("inspectorSectionHeader");
|
||||||
|
auto* header_layout = new QHBoxLayout(header);
|
||||||
|
header_layout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
header_layout->setSpacing(6);
|
||||||
|
|
||||||
|
auto* toggle = new QToolButton(header);
|
||||||
|
toggle->setObjectName("inspectorSectionButton");
|
||||||
|
toggle->setText(title);
|
||||||
|
toggle->setCheckable(true);
|
||||||
|
toggle->setChecked(true);
|
||||||
|
toggle->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
||||||
|
toggle->setArrowType(Qt::DownArrow);
|
||||||
|
header_layout->addWidget(toggle);
|
||||||
|
header_layout->addStretch(1);
|
||||||
|
|
||||||
|
QLineEdit* filter_field = nullptr;
|
||||||
|
if (!filter_placeholder.isEmpty()) {
|
||||||
|
auto* filter_toggle = new QToolButton(header);
|
||||||
|
filter_toggle->setObjectName("inspectorFilterToggle");
|
||||||
|
filter_toggle->setCheckable(true);
|
||||||
|
filter_toggle->setIcon(makePanelSvgIcon(":/icons/filter.svg"));
|
||||||
|
filter_toggle->setAutoRaise(true);
|
||||||
|
header_layout->addWidget(filter_toggle);
|
||||||
|
|
||||||
|
filter_field = qobject_cast<QLineEdit*>(makeInspectorFilterField(filter_placeholder, section));
|
||||||
|
filter_field->setVisible(false);
|
||||||
|
QObject::connect(filter_toggle, &QToolButton::toggled, filter_field, [filter_field](bool visible) {
|
||||||
|
filter_field->setVisible(visible);
|
||||||
|
if (visible) filter_field->setFocus();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* body = new QWidget(section);
|
||||||
|
body->setObjectName("inspectorSectionBody");
|
||||||
|
auto* body_layout = new QVBoxLayout(body);
|
||||||
|
body_layout->setContentsMargins(10, 6, 10, 0);
|
||||||
|
body_layout->setSpacing(6);
|
||||||
|
for (auto* group : groups) body_layout->addWidget(group);
|
||||||
|
|
||||||
|
QObject::connect(toggle, &QToolButton::toggled, body, [toggle, body](bool expanded) {
|
||||||
|
toggle->setArrowType(expanded ? Qt::DownArrow : Qt::RightArrow);
|
||||||
|
body->setVisible(expanded);
|
||||||
|
});
|
||||||
|
|
||||||
|
layout->addWidget(header);
|
||||||
|
if (filter_field) {
|
||||||
|
auto* filter_wrapper = new QWidget(section);
|
||||||
|
filter_wrapper->setObjectName("inspectorFilterWrapper");
|
||||||
|
auto* filter_wrapper_layout = new QVBoxLayout(filter_wrapper);
|
||||||
|
filter_wrapper_layout->setContentsMargins(10, 0, 10, 0);
|
||||||
|
filter_wrapper_layout->setSpacing(0);
|
||||||
|
filter_wrapper_layout->addWidget(filter_field);
|
||||||
|
layout->addWidget(filter_wrapper);
|
||||||
|
}
|
||||||
|
layout->addWidget(body);
|
||||||
|
return section;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
return panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
return panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace ifcinterface::panels::properties {
|
||||||
|
|
||||||
|
PropertiesPanelWidget::PropertiesPanelWidget(QWidget* parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
{
|
||||||
|
auto* root = new QVBoxLayout(this);
|
||||||
|
root->setContentsMargins(0, 0, 0, 0);
|
||||||
|
root->setSpacing(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PropertiesPanelWidget::setState(const PropertiesPanelState& state) {
|
||||||
|
auto* root = qobject_cast<QVBoxLayout*>(layout());
|
||||||
|
while (auto* item = root->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);
|
||||||
|
entity_card->setObjectName("entityClassCard");
|
||||||
|
auto* entity_layout = new QHBoxLayout(entity_card);
|
||||||
|
entity_layout->setContentsMargins(10, 8, 10, 8);
|
||||||
|
entity_layout->setSpacing(10);
|
||||||
|
auto* entity_icon = new QLabel(entity_card);
|
||||||
|
entity_icon->setPixmap(makePanelSvgPixmap(":/icons/cube-dots.svg", QSize(28, 28)));
|
||||||
|
entity_icon->setAlignment(Qt::AlignCenter);
|
||||||
|
auto* entity_text = new QWidget(entity_card);
|
||||||
|
auto* entity_text_layout = new QVBoxLayout(entity_text);
|
||||||
|
entity_text_layout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
entity_text_layout->setSpacing(2);
|
||||||
|
auto* entity_class = new QLabel(state.entity.entity_class, entity_text);
|
||||||
|
entity_class->setObjectName("entityClassLabel");
|
||||||
|
auto* entity_type = new QLabel(state.entity.predefined_type, entity_text);
|
||||||
|
entity_type->setObjectName("entityTypeLabel");
|
||||||
|
entity_text_layout->addWidget(entity_class);
|
||||||
|
entity_text_layout->addWidget(entity_type);
|
||||||
|
entity_layout->addWidget(entity_icon, 0, Qt::AlignVCenter);
|
||||||
|
entity_layout->addWidget(entity_text, 1, Qt::AlignVCenter);
|
||||||
|
|
||||||
|
auto* entity_wrapper = new QWidget(content);
|
||||||
|
entity_wrapper->setObjectName("inspectorSectionBody");
|
||||||
|
auto* entity_wrapper_layout = new QVBoxLayout(entity_wrapper);
|
||||||
|
entity_wrapper_layout->setContentsMargins(10, 0, 10, 0);
|
||||||
|
entity_wrapper_layout->setSpacing(0);
|
||||||
|
entity_wrapper_layout->addWidget(entity_card);
|
||||||
|
|
||||||
|
QList<QWidget*> property_set_widgets;
|
||||||
|
for (const auto& property_set : state.property_sets) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* attributes_section = makeInspectorSection(
|
||||||
|
"Attributes", "", {makeAttributeList(state.attributes, content)}, content);
|
||||||
|
auto* relationships_section = makeInspectorSection(
|
||||||
|
"Relationships", "", {makeRelationshipList(state.relationships, content)}, content);
|
||||||
|
auto* properties_section = makeInspectorSection(
|
||||||
|
"Properties", "Filter properties or sets", property_set_widgets, content);
|
||||||
|
auto* quantities_section = makeInspectorSection(
|
||||||
|
"Quantities", "Filter quantities or sets", quantity_set_widgets, content);
|
||||||
|
|
||||||
|
content_layout->addWidget(entity_wrapper);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ifcinterface::panels::properties
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
// 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_PROPERTIESPANELWIDGET_H
|
||||||
|
#define IFCINTERFACE_PANELS_PROPERTIESPANELWIDGET_H
|
||||||
|
|
||||||
|
#include "PropertiesPanelTypes.h"
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
namespace ifcinterface::panels::properties {
|
||||||
|
|
||||||
|
class PropertiesPanelWidget : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit PropertiesPanelWidget(QWidget* parent = nullptr);
|
||||||
|
|
||||||
|
void setState(const PropertiesPanelState& state);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ifcinterface::panels::properties
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
// 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_SPATIALHIERARCHYPANELTYPES_H
|
||||||
|
#define IFCINTERFACE_PANELS_SPATIALHIERARCHYPANELTYPES_H
|
||||||
|
|
||||||
|
#include <QList>
|
||||||
|
#include <QString>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
namespace ifcinterface::panels::spatial_hierarchy {
|
||||||
|
|
||||||
|
enum class ItemKind {
|
||||||
|
Site,
|
||||||
|
Building,
|
||||||
|
Storey,
|
||||||
|
Space,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TreeNode {
|
||||||
|
QString name;
|
||||||
|
ItemKind kind = ItemKind::Space;
|
||||||
|
bool visible = true;
|
||||||
|
QList<TreeNode> children;
|
||||||
|
};
|
||||||
|
|
||||||
|
using NodePath = QStringList;
|
||||||
|
|
||||||
|
} // namespace ifcinterface::panels::spatial_hierarchy
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -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/>. *
|
||||||
|
* *
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
#include "SpatialHierarchyPanelView.h"
|
||||||
|
|
||||||
|
#include "SpatialHierarchyPanelWidget.h"
|
||||||
|
|
||||||
|
namespace ifcinterface::panels::spatial_hierarchy {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
TreeNode* findNodeRecursive(QList<TreeNode>& nodes, const NodePath& path, int depth) {
|
||||||
|
for (auto& node : nodes) {
|
||||||
|
if (node.name != path.at(depth)) continue;
|
||||||
|
if (depth == path.size() - 1) return &node;
|
||||||
|
return findNodeRecursive(node.children, path, depth + 1);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
SpatialHierarchyPanelView::SpatialHierarchyPanelView(SpatialHierarchyPanelWidget* widget, QObject* parent)
|
||||||
|
: QObject(parent), widget_(widget)
|
||||||
|
{
|
||||||
|
nodes_ = {
|
||||||
|
{"Site A", ItemKind::Site, true,
|
||||||
|
{{"Building 01", ItemKind::Building, true,
|
||||||
|
{{"Level 02", ItemKind::Storey, true,
|
||||||
|
{{"Lobby", ItemKind::Space, true, {}},
|
||||||
|
{"Core", ItemKind::Space, true, {}}}}}}}},
|
||||||
|
};
|
||||||
|
|
||||||
|
connect(widget_, &SpatialHierarchyPanelWidget::visibilityToggleRequested, this, [this](const NodePath& path) {
|
||||||
|
if (auto* node = findNode(path)) {
|
||||||
|
node->visible = !node->visible;
|
||||||
|
reload();
|
||||||
|
emit statusMessageRequested("Spatial", node->visible ? "Item shown" : "Item hidden");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpatialHierarchyPanelView::reload() {
|
||||||
|
widget_->setNodes(nodes_);
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeNode* SpatialHierarchyPanelView::findNode(const NodePath& path) {
|
||||||
|
if (path.isEmpty()) return nullptr;
|
||||||
|
return findNodeRecursive(nodes_, path, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ifcinterface::panels::spatial_hierarchy
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
// 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_SPATIALHIERARCHYPANELVIEW_H
|
||||||
|
#define IFCINTERFACE_PANELS_SPATIALHIERARCHYPANELVIEW_H
|
||||||
|
|
||||||
|
#include "SpatialHierarchyPanelTypes.h"
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
namespace ifcinterface::panels::spatial_hierarchy {
|
||||||
|
|
||||||
|
class SpatialHierarchyPanelWidget;
|
||||||
|
|
||||||
|
class SpatialHierarchyPanelView : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit SpatialHierarchyPanelView(SpatialHierarchyPanelWidget* widget, QObject* parent = nullptr);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void statusMessageRequested(const QString& mode, const QString& detail);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void reload();
|
||||||
|
TreeNode* findNode(const NodePath& path);
|
||||||
|
|
||||||
|
SpatialHierarchyPanelWidget* widget_ = nullptr;
|
||||||
|
QList<TreeNode> nodes_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ifcinterface::panels::spatial_hierarchy
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
// 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 "SpatialHierarchyPanelWidget.h"
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
|
#include <QHeaderView>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QPixmap>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QSvgRenderer>
|
||||||
|
#include <QTreeWidget>
|
||||||
|
#include <QTreeWidgetItem>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
QPixmap renderPanelSvgPixmap(const QString& icon_path, const QString& color, const QSize& size) {
|
||||||
|
QFile file(icon_path);
|
||||||
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
|
return QIcon(icon_path).pixmap(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString svg = QString::fromUtf8(file.readAll());
|
||||||
|
svg.replace("currentColor", color, Qt::CaseSensitive);
|
||||||
|
svg.replace(QRegularExpression(R"(stroke="[^"]*")"), QString("stroke=\"%1\"").arg(color));
|
||||||
|
svg.replace(QRegularExpression(R"(fill="none")"), "fill=\"none\"");
|
||||||
|
|
||||||
|
QSvgRenderer renderer(svg.toUtf8());
|
||||||
|
QPixmap pixmap(size);
|
||||||
|
pixmap.fill(Qt::transparent);
|
||||||
|
QPainter painter(&pixmap);
|
||||||
|
renderer.render(&painter);
|
||||||
|
return pixmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
QIcon makePanelSvgIcon(const QString& icon_path) {
|
||||||
|
QIcon icon;
|
||||||
|
icon.addPixmap(renderPanelSvgPixmap(icon_path, "#e7ebf2", QSize(20, 20)), QIcon::Normal, QIcon::Off);
|
||||||
|
icon.addPixmap(renderPanelSvgPixmap(icon_path, "#ffffff", QSize(20, 20)), QIcon::Active, QIcon::Off);
|
||||||
|
icon.addPixmap(renderPanelSvgPixmap(icon_path, "#ffffff", QSize(20, 20)), QIcon::Selected, QIcon::Off);
|
||||||
|
icon.addPixmap(renderPanelSvgPixmap(icon_path, "#6f7988", QSize(20, 20)), QIcon::Disabled, QIcon::Off);
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace ifcinterface::panels::spatial_hierarchy {
|
||||||
|
|
||||||
|
SpatialHierarchyPanelWidget::SpatialHierarchyPanelWidget(QWidget* parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
{
|
||||||
|
auto* layout = new QVBoxLayout(this);
|
||||||
|
layout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
layout->setSpacing(0);
|
||||||
|
|
||||||
|
tree_ = new QTreeWidget(this);
|
||||||
|
tree_->setColumnCount(2);
|
||||||
|
tree_->setHeaderLabels({"Spatial Item", ""});
|
||||||
|
tree_->setIconSize(QSize(16, 16));
|
||||||
|
tree_->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||||
|
tree_->setUniformRowHeights(true);
|
||||||
|
tree_->header()->setStretchLastSection(false);
|
||||||
|
tree_->header()->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||||
|
tree_->header()->setSectionResizeMode(1, QHeaderView::Fixed);
|
||||||
|
tree_->header()->resizeSection(1, 28);
|
||||||
|
tree_->header()->hide();
|
||||||
|
layout->addWidget(tree_);
|
||||||
|
|
||||||
|
connect(tree_, &QTreeWidget::itemClicked, this, [this](QTreeWidgetItem* item, int column) {
|
||||||
|
if (!item || column != 1) return;
|
||||||
|
emit visibilityToggleRequested(itemPath(item));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpatialHierarchyPanelWidget::setNodes(const QList<TreeNode>& nodes) {
|
||||||
|
tree_->clear();
|
||||||
|
for (const auto& node : nodes) {
|
||||||
|
addNode(tree_->invisibleRootItem(), node);
|
||||||
|
}
|
||||||
|
tree_->expandAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpatialHierarchyPanelWidget::addNode(QTreeWidgetItem* parent, const TreeNode& node) {
|
||||||
|
auto* item = new QTreeWidgetItem(parent, {node.name, ""});
|
||||||
|
item->setData(1, Qt::UserRole, node.visible);
|
||||||
|
item->setSizeHint(0, QSize(0, 24));
|
||||||
|
item->setIcon(0, makePanelSvgIcon(iconPath(node.kind)));
|
||||||
|
item->setIcon(1, makePanelSvgIcon(node.visible ? ":/icons/eye.svg" : ":/icons/eye-closed.svg"));
|
||||||
|
for (const auto& child : node.children) {
|
||||||
|
addNode(item, child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NodePath SpatialHierarchyPanelWidget::itemPath(QTreeWidgetItem* item) const {
|
||||||
|
NodePath path;
|
||||||
|
while (item) {
|
||||||
|
path.prepend(item->text(0));
|
||||||
|
item = item->parent();
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString SpatialHierarchyPanelWidget::iconPath(ItemKind kind) const {
|
||||||
|
switch (kind) {
|
||||||
|
case ItemKind::Site: return ":/icons/frame-alt.svg";
|
||||||
|
case ItemKind::Building: return ":/icons/city.svg";
|
||||||
|
case ItemKind::Storey: return ":/icons/planimetry.svg";
|
||||||
|
case ItemKind::Space: return ":/icons/square3d-from-center.svg";
|
||||||
|
}
|
||||||
|
return ":/icons/frame-alt.svg";
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ifcinterface::panels::spatial_hierarchy
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
// 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_SPATIALHIERARCHYPANELWIDGET_H
|
||||||
|
#define IFCINTERFACE_PANELS_SPATIALHIERARCHYPANELWIDGET_H
|
||||||
|
|
||||||
|
#include "SpatialHierarchyPanelTypes.h"
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
class QTreeWidget;
|
||||||
|
class QTreeWidgetItem;
|
||||||
|
|
||||||
|
namespace ifcinterface::panels::spatial_hierarchy {
|
||||||
|
|
||||||
|
class SpatialHierarchyPanelWidget : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit SpatialHierarchyPanelWidget(QWidget* parent = nullptr);
|
||||||
|
|
||||||
|
void setNodes(const QList<TreeNode>& nodes);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void visibilityToggleRequested(const NodePath& path);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void addNode(QTreeWidgetItem* parent, const TreeNode& node);
|
||||||
|
NodePath itemPath(QTreeWidgetItem* item) const;
|
||||||
|
QString iconPath(ItemKind kind) const;
|
||||||
|
|
||||||
|
QTreeWidget* tree_ = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ifcinterface::panels::spatial_hierarchy
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user