mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-07 04:46:25 +00:00
spatial hierarchy from IFC + active-model concept
Build the spatial hierarchy panel from the loaded model's real IFC spatial structure instead of mock data: - helpers/element: add get_spatial_children (IsDecomposedBy -> RelatedObjects, filtered to spatial elements) to walk IfcProject -> IfcSite -> IfcBuilding -> IfcBuildingStorey -> IfcSpace. - SessionState: relay dataSourceReady as modelDataSourceReady (the .ifc for a sidecar hit loads asynchronously, so the tree can only build once it arrives). - spatial_hierarchy/View: walk the active model's IFC file into a TreeNode tree, naming nodes by Name (fallback to class), mapping site/building/storey kinds; siblings sorted with natural (numeric) collation. - spatial_hierarchy/Panel: tree now fills the panel height (setBodyExpanding + Expanding size policy); right-click menu for recursive Expand/Collapse Subtree and Expand/Collapse All. Add the concept of an active model: - SessionState: activeModelId / setActiveModelId / activeModelChanged; the first loaded model is active by default; reassigns/clears on removal. - Models panel: clicking a model makes it active; its cube icon is drawn with the accent colour (makeAccentSvgIcon) via FederationItemModel::setActiveModelId. - The spatial hierarchy reflects only the active model. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -87,14 +87,28 @@ QStandardItem* FederationItemModel::makeGroupNameItem(const QString& group_id, c
|
||||
return item;
|
||||
}
|
||||
|
||||
QIcon FederationItemModel::modelIcon(const QString& model_id) const {
|
||||
return model_id == active_model_id_
|
||||
? components::icons::makeAccentSvgIcon(":/icons/cube.svg")
|
||||
: components::icons::makeSvgIcon(":/icons/cube.svg");
|
||||
}
|
||||
|
||||
QStandardItem* FederationItemModel::makeModelNameItem(const QString& model_id, const QString& display_name) const {
|
||||
auto* item = new QStandardItem(components::icons::makeSvgIcon(":/icons/cube.svg"), display_name);
|
||||
auto* item = new QStandardItem(modelIcon(model_id), display_name);
|
||||
item->setData(model_id, IdRole);
|
||||
item->setData(int(ItemKind::Model), KindRole);
|
||||
item->setEditable(false);
|
||||
return item;
|
||||
}
|
||||
|
||||
void FederationItemModel::setActiveModelId(const QString& model_id) {
|
||||
if (model_id == active_model_id_) return;
|
||||
const QString previous = active_model_id_;
|
||||
active_model_id_ = model_id;
|
||||
if (auto* item = id_to_name_item_.value(previous)) item->setIcon(modelIcon(previous));
|
||||
if (auto* item = id_to_name_item_.value(active_model_id_)) item->setIcon(modelIcon(active_model_id_));
|
||||
}
|
||||
|
||||
QStandardItem* FederationItemModel::makeVisibilityItem(ItemKind kind, bool visible) const {
|
||||
QString icon_path;
|
||||
if (kind == ItemKind::Group) {
|
||||
|
||||
@@ -53,6 +53,10 @@ public:
|
||||
// preserving anyway).
|
||||
void rebuildAll();
|
||||
|
||||
// The active model is drawn with an accent-coloured cube icon. Restyles the
|
||||
// previously- and newly-active model rows.
|
||||
void setActiveModelId(const QString& model_id);
|
||||
|
||||
private slots:
|
||||
void onGroupAdded(const QString& group_id);
|
||||
void onGroupRemoved(const QString& group_id);
|
||||
@@ -77,8 +81,11 @@ private:
|
||||
void appendGroupSubtreeTo(QStandardItem* parent_item, const QString& group_id);
|
||||
void refreshSubtreeVisibility(QStandardItem* root);
|
||||
|
||||
QIcon modelIcon(const QString& model_id) const; // accent cube when active, else plain
|
||||
|
||||
Federation* federation_ = nullptr;
|
||||
QHash<QString, QStandardItem*> id_to_name_item_; // both group_ids and model_ids
|
||||
QString active_model_id_;
|
||||
};
|
||||
|
||||
} // namespace bonsaiviewer::modules::models
|
||||
|
||||
@@ -249,8 +249,15 @@ ModelsPanel::ModelsPanel(bonsaiviewer::SessionState* session_state,
|
||||
addBodyWidget(section);
|
||||
|
||||
connect(tree_, &QTreeView::clicked, this, [this](const QModelIndex& index) {
|
||||
if (!index.isValid() || index.column() != 1) return;
|
||||
commands::toggleVisibility(*session_state_, kindOf(index), idOf(index));
|
||||
if (!index.isValid()) return;
|
||||
if (index.column() == 1) {
|
||||
commands::toggleVisibility(*session_state_, kindOf(index), idOf(index));
|
||||
return;
|
||||
}
|
||||
// Clicking a model (its cube icon / row) makes it the active model.
|
||||
if (kindOf(index) == ItemKind::Model) {
|
||||
session_state_->setActiveModelId(idOf(index));
|
||||
}
|
||||
});
|
||||
|
||||
connect(tree_, &QTreeView::customContextMenuRequested, this, [this](const QPoint& pos) {
|
||||
|
||||
@@ -70,6 +70,9 @@ ModelsPanelView::ModelsPanelView(ModelsPanel* widget,
|
||||
connect(&bonsaiviewer::ViewerSettings::instance(),
|
||||
&bonsaiviewer::ViewerSettings::themeChanged,
|
||||
this, rebuild);
|
||||
connect(session_state_, &SessionState::activeModelChanged, this, [this](const QString& model_id) {
|
||||
model_->setActiveModelId(model_id);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace bonsaiviewer::modules::models
|
||||
|
||||
Reference in New Issue
Block a user