mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
properties: real empty states + smaller base UI font
- Replace the mock IfcWall placeholder with a "No item selected" empty state; the panel only fills in class/attributes/relationships/psets from a resolved object, and safely stays empty otherwise. - Show "No properties" / "No quantities" placeholders (muted, themed via secondary_text) when those sets are empty. - Drop the base application font 10pt -> 9pt to fit more data. Panel titles keep their own explicit size and are unaffected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -143,6 +143,10 @@ QString buildAppStyleSheet() {
|
||||
font-size: ${font_small}px;
|
||||
font-weight: 600;
|
||||
}
|
||||
QLabel#panelSectionEmptyLabel {
|
||||
color: ${secondary_text};
|
||||
padding: 4px 10px;
|
||||
}
|
||||
QToolButton#panelTitleButton {
|
||||
border: none;
|
||||
background: transparent;
|
||||
|
||||
@@ -41,7 +41,10 @@ void installUiFont() {
|
||||
}
|
||||
}
|
||||
if (!family.isEmpty()) {
|
||||
QApplication::setFont(QFont(family, 10));
|
||||
// Slightly smaller base font to fit more data. Panel titles keep their
|
||||
// own explicit size (QLabel#panelTitleText in Style.cpp), so they're
|
||||
// unaffected by this.
|
||||
QApplication::setFont(QFont(family, 9));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,12 @@ QWidget* makeRelationshipList(const QList<bonsaiviewer::modules::properties::Rel
|
||||
return new bonsaiviewer::components::KeyValueTable(table_rows, parent);
|
||||
}
|
||||
|
||||
QLabel* makeEmptyStateLabel(const QString& text, QWidget* parent = nullptr) {
|
||||
auto* label = new QLabel(text, parent);
|
||||
label->setObjectName("panelSectionEmptyLabel");
|
||||
return label;
|
||||
}
|
||||
|
||||
QWidget* makeFilterWrapper(QLineEdit** field_out, QWidget* parent = nullptr) {
|
||||
auto* wrapper = new QWidget(parent);
|
||||
wrapper->setObjectName("panelSectionFilterWrapper");
|
||||
@@ -176,6 +182,9 @@ void PropertiesPanel::render(const PropertiesPanelState& state) {
|
||||
});
|
||||
properties_section->addBodyWidget(properties_filter_wrapper);
|
||||
for (auto* widget : property_set_widgets) properties_section->addBodyWidget(widget);
|
||||
if (property_set_widgets.isEmpty()) {
|
||||
properties_section->addBodyWidget(makeEmptyStateLabel("No properties", this));
|
||||
}
|
||||
properties_section->setExpanded(properties_expanded_);
|
||||
properties_filter_toggle->setChecked(properties_filter_visible_);
|
||||
|
||||
@@ -203,6 +212,9 @@ void PropertiesPanel::render(const PropertiesPanelState& state) {
|
||||
});
|
||||
quantities_section->addBodyWidget(quantities_filter_wrapper);
|
||||
for (auto* widget : quantity_set_widgets) quantities_section->addBodyWidget(widget);
|
||||
if (quantity_set_widgets.isEmpty()) {
|
||||
quantities_section->addBodyWidget(makeEmptyStateLabel("No quantities", this));
|
||||
}
|
||||
quantities_section->setExpanded(quantities_expanded_);
|
||||
quantities_filter_toggle->setChecked(quantities_filter_visible_);
|
||||
|
||||
|
||||
@@ -102,60 +102,14 @@ PropertiesPanelView::PropertiesPanelView(PropertiesPanel* widget,
|
||||
|
||||
void PropertiesPanelView::refresh(uint32_t object_id) {
|
||||
auto* registry = session_state_->elementRegistry();
|
||||
|
||||
// Empty default: nothing selected → "No item selected" with empty sections.
|
||||
// Real data is filled in below when an object resolves.
|
||||
PropertiesPanelState state;
|
||||
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"}}},
|
||||
};
|
||||
state.entity = {"No item selected", ""};
|
||||
|
||||
if (!registry) {
|
||||
widget_->render(state);
|
||||
return;
|
||||
}
|
||||
|
||||
// A real project is loaded — don't leak the placeholder attributes /
|
||||
// relationships / predefined type. They're populated below from live IFC
|
||||
// data, or from the cached basics for geometry-only elements.
|
||||
state.entity.predefined_type.clear();
|
||||
state.attributes.clear();
|
||||
state.relationships.clear();
|
||||
state.property_sets.clear();
|
||||
state.quantity_sets.clear();
|
||||
|
||||
auto entity = registry->findEntity(object_id);
|
||||
auto entity = registry ? registry->findEntity(object_id)
|
||||
: std::optional<express::Base>{};
|
||||
if (entity) {
|
||||
state.entity.entity_class = QString::fromStdString(entity->declaration().name());
|
||||
if (auto predefined_type = get_predefined_type(*entity)) {
|
||||
@@ -183,7 +137,7 @@ void PropertiesPanelView::refresh(uint32_t object_id) {
|
||||
// occurrence values inheriting from the type.
|
||||
state.property_sets = toPropertySets(get_psets(*entity, /*psets_only=*/true, /*qtos_only=*/false));
|
||||
state.quantity_sets = toPropertySets(get_psets(*entity, /*psets_only=*/false, /*qtos_only=*/true));
|
||||
} else {
|
||||
} else if (registry) {
|
||||
// No live IFC source for this object — typical when a pure-geometry
|
||||
// .ifcview sidecar was loaded without its .ifc/.rdb sibling. Fall back
|
||||
// to the basic info cached in the element registry so the panel still
|
||||
|
||||
Reference in New Issue
Block a user