Interface mockup 8

This commit is contained in:
Dion Moult
2026-05-06 21:35:12 +10:00
parent 5b2721c1c4
commit f07afda09c
28 changed files with 804 additions and 350 deletions
+19 -17
View File
@@ -39,23 +39,25 @@ set(INTERFACE_FILES
${CMAKE_CURRENT_SOURCE_DIR}/components/Section.h ${CMAKE_CURRENT_SOURCE_DIR}/components/Section.h
${CMAKE_CURRENT_SOURCE_DIR}/components/Panel.cpp ${CMAKE_CURRENT_SOURCE_DIR}/components/Panel.cpp
${CMAKE_CURRENT_SOURCE_DIR}/components/Panel.h ${CMAKE_CURRENT_SOURCE_DIR}/components/Panel.h
${CMAKE_CURRENT_SOURCE_DIR}/panels/models/ModelsPanelTypes.h ${CMAKE_CURRENT_SOURCE_DIR}/panels/models/Types.h
${CMAKE_CURRENT_SOURCE_DIR}/panels/models/ModelsPanelWidget.cpp ${CMAKE_CURRENT_SOURCE_DIR}/panels/models/Widget.cpp
${CMAKE_CURRENT_SOURCE_DIR}/panels/models/ModelsPanelWidget.h ${CMAKE_CURRENT_SOURCE_DIR}/panels/models/Widget.h
${CMAKE_CURRENT_SOURCE_DIR}/panels/models/ModelsPanelView.cpp ${CMAKE_CURRENT_SOURCE_DIR}/panels/models/View.cpp
${CMAKE_CURRENT_SOURCE_DIR}/panels/models/ModelsPanelView.h ${CMAKE_CURRENT_SOURCE_DIR}/panels/models/View.h
${CMAKE_CURRENT_SOURCE_DIR}/panels/todo/TodoPanelWidget.cpp ${CMAKE_CURRENT_SOURCE_DIR}/panels/todo/Widget.cpp
${CMAKE_CURRENT_SOURCE_DIR}/panels/todo/TodoPanelWidget.h ${CMAKE_CURRENT_SOURCE_DIR}/panels/todo/Widget.h
${CMAKE_CURRENT_SOURCE_DIR}/panels/properties/PropertiesPanelTypes.h ${CMAKE_CURRENT_SOURCE_DIR}/panels/properties/Types.h
${CMAKE_CURRENT_SOURCE_DIR}/panels/properties/PropertiesPanelWidget.cpp ${CMAKE_CURRENT_SOURCE_DIR}/panels/properties/Widget.cpp
${CMAKE_CURRENT_SOURCE_DIR}/panels/properties/PropertiesPanelWidget.h ${CMAKE_CURRENT_SOURCE_DIR}/panels/properties/Widget.h
${CMAKE_CURRENT_SOURCE_DIR}/panels/properties/PropertiesPanelView.cpp ${CMAKE_CURRENT_SOURCE_DIR}/panels/properties/View.cpp
${CMAKE_CURRENT_SOURCE_DIR}/panels/properties/PropertiesPanelView.h ${CMAKE_CURRENT_SOURCE_DIR}/panels/properties/View.h
${CMAKE_CURRENT_SOURCE_DIR}/panels/spatial_hierarchy/SpatialHierarchyPanelTypes.h ${CMAKE_CURRENT_SOURCE_DIR}/panels/settings/Dialog.cpp
${CMAKE_CURRENT_SOURCE_DIR}/panels/spatial_hierarchy/SpatialHierarchyPanelWidget.cpp ${CMAKE_CURRENT_SOURCE_DIR}/panels/settings/Dialog.h
${CMAKE_CURRENT_SOURCE_DIR}/panels/spatial_hierarchy/SpatialHierarchyPanelWidget.h ${CMAKE_CURRENT_SOURCE_DIR}/panels/spatial_hierarchy/Types.h
${CMAKE_CURRENT_SOURCE_DIR}/panels/spatial_hierarchy/SpatialHierarchyPanelView.cpp ${CMAKE_CURRENT_SOURCE_DIR}/panels/spatial_hierarchy/Widget.cpp
${CMAKE_CURRENT_SOURCE_DIR}/panels/spatial_hierarchy/SpatialHierarchyPanelView.h ${CMAKE_CURRENT_SOURCE_DIR}/panels/spatial_hierarchy/Widget.h
${CMAKE_CURRENT_SOURCE_DIR}/panels/spatial_hierarchy/View.cpp
${CMAKE_CURRENT_SOURCE_DIR}/panels/spatial_hierarchy/View.h
${CMAKE_CURRENT_SOURCE_DIR}/interface_resources.qrc ${CMAKE_CURRENT_SOURCE_DIR}/interface_resources.qrc
) )
+10 -9
View File
@@ -27,13 +27,14 @@
#include "components/Panel.h" #include "components/Panel.h"
#include "components/Style.h" #include "components/Style.h"
#include "components/SvgIcon.h" #include "components/SvgIcon.h"
#include "panels/todo/TodoPanelWidget.h" #include "panels/todo/Widget.h"
#include "panels/models/ModelsPanelView.h" #include "panels/models/View.h"
#include "panels/models/ModelsPanelWidget.h" #include "panels/models/Widget.h"
#include "panels/properties/PropertiesPanelView.h" #include "panels/properties/View.h"
#include "panels/properties/PropertiesPanelWidget.h" #include "panels/properties/Widget.h"
#include "panels/spatial_hierarchy/SpatialHierarchyPanelView.h" #include "panels/settings/Dialog.h"
#include "panels/spatial_hierarchy/SpatialHierarchyPanelWidget.h" #include "panels/spatial_hierarchy/View.h"
#include "panels/spatial_hierarchy/Widget.h"
#include <QDockWidget> #include <QDockWidget>
#include <QFileDialog> #include <QFileDialog>
@@ -169,7 +170,8 @@ QWidget* MainWindow::buildHomeRibbonPage() {
auto* settings_button = makeRibbonAction("Settings", ":/icons/settings.svg"); auto* settings_button = makeRibbonAction("Settings", ":/icons/settings.svg");
connect(settings_button, &QToolButton::clicked, this, [this]() { connect(settings_button, &QToolButton::clicked, this, [this]() {
setStatusMessage("Settings", "Settings coming soon"); panels::settings::SettingsDialog dialog(this);
dialog.exec();
}); });
row->addWidget(makeRibbonGroup("PROJECT", {new_project, open_project, open_cloud, open_recent, save_project, save_project_as})); row->addWidget(makeRibbonGroup("PROJECT", {new_project, open_project, open_cloud, open_recent, save_project, save_project_as}));
@@ -454,7 +456,6 @@ void MainWindow::setupStatus() {
} }
void MainWindow::setupLoader() { void MainWindow::setupLoader() {
AppSettings::instance().setLoadDataSource(false);
loader_ = new SceneLoader(viewport_, this); loader_ = new SceneLoader(viewport_, this);
element_registry_->bindLoader(loader_); element_registry_->bindLoader(loader_);
connect(loader_, &SceneLoader::loadStarted, this, &MainWindow::onLoadStarted); connect(loader_, &SceneLoader::loadStarted, this, &MainWindow::onLoadStarted);
+1 -1
View File
@@ -61,7 +61,7 @@ KeyValueTable::KeyValueTable(const QList<KeyValueTableRow>& rows, QWidget* paren
icon->setObjectName(row_data.trailing_icon_object_name.isEmpty() icon->setObjectName(row_data.trailing_icon_object_name.isEmpty()
? "keyValueTrailingIconLabel" ? "keyValueTrailingIconLabel"
: row_data.trailing_icon_object_name); : row_data.trailing_icon_object_name);
icon->setPixmap(icons::makePanelSvgPixmap(row_data.trailing_icon_path, QSize(14, 14))); icon->setPixmap(icons::makeSvgPixmap(row_data.trailing_icon_path, QSize(14, 14)));
icon->setAlignment(Qt::AlignRight | Qt::AlignVCenter); icon->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
layout->addWidget(icon, row_index, 2, Qt::AlignRight | Qt::AlignTop); layout->addWidget(icon, row_index, 2, Qt::AlignRight | Qt::AlignTop);
} }
+1 -1
View File
@@ -52,7 +52,7 @@ public:
layout->addStretch(1); layout->addStretch(1);
if (has_settings) { if (has_settings) {
auto* settings = new QToolButton(this); auto* settings = new QToolButton(this);
settings->setIcon(icons::makePanelSvgIcon(":/icons/settings.svg")); settings->setIcon(icons::makeSvgIcon(":/icons/settings.svg"));
settings->setAutoRaise(true); settings->setAutoRaise(true);
settings->setCursor(Qt::ArrowCursor); settings->setCursor(Qt::ArrowCursor);
settings->setFixedSize(18, 18); settings->setFixedSize(18, 18);
+36 -74
View File
@@ -21,30 +21,15 @@
#include "Section.h" #include "Section.h"
#include "Style.h" #include "Style.h"
#include "SvgIcon.h"
#include <QFrame> #include <QFrame>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QLineEdit>
#include <QToolButton> #include <QToolButton>
#include <QVBoxLayout> #include <QVBoxLayout>
namespace ifcinterface::components { namespace ifcinterface::components {
namespace { Section::Section(const QString& title, SectionHeaderMode header_mode, QWidget* parent)
QWidget* makeSectionFilterField(const QString& placeholder, QWidget* parent = nullptr) {
auto* field = new QLineEdit(parent);
field->setPlaceholderText(placeholder);
field->setClearButtonEnabled(true);
field->addAction(icons::makePanelSvgIcon(":/icons/filter.svg"), QLineEdit::LeadingPosition);
return field;
}
} // namespace
Section::Section(const QString& title, SectionHeaderMode header_mode,
const QString& filter_placeholder, QWidget* parent)
: QWidget(parent) : QWidget(parent)
{ {
setObjectName("panelSection"); setObjectName("panelSection");
@@ -55,70 +40,26 @@ Section::Section(const QString& title, SectionHeaderMode header_mode,
if (header_mode == SectionHeaderMode::Visible) { if (header_mode == SectionHeaderMode::Visible) {
auto* header = new QFrame(this); auto* header = new QFrame(this);
header->setObjectName("panelSectionHeader"); header->setObjectName("panelSectionHeader");
auto* header_layout = new QHBoxLayout(header); header_layout_ = new QHBoxLayout(header);
header_layout->setContentsMargins(0, 0, 0, 0); header_layout_->setContentsMargins(0, 0, 0, 0);
header_layout->setSpacing(style::metrics::padding); header_layout_->setSpacing(style::metrics::padding);
auto* toggle = new QToolButton(header); toggle_button_ = new QToolButton(header);
toggle->setObjectName("panelSectionHeaderButton"); toggle_button_->setObjectName("panelSectionHeaderButton");
toggle->setText(title); toggle_button_->setText(title);
toggle->setCheckable(true); toggle_button_->setCheckable(true);
toggle->setChecked(true); toggle_button_->setChecked(true);
toggle->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); toggle_button_->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
toggle->setArrowType(Qt::DownArrow); toggle_button_->setArrowType(Qt::DownArrow);
header_layout->addWidget(toggle); header_layout_->addWidget(toggle_button_);
header_layout->addStretch(1); header_layout_->addStretch(1);
if (!filter_placeholder.isEmpty()) {
auto* filter_toggle = new QToolButton(header);
filter_toggle->setObjectName("panelSectionFilterToggle");
filter_toggle->setCheckable(true);
filter_toggle->setIcon(icons::makePanelSvgIcon(":/icons/filter.svg"));
filter_toggle->setAutoRaise(true);
header_layout->addWidget(filter_toggle);
filter_field_ = qobject_cast<QLineEdit*>(makeSectionFilterField(filter_placeholder, this));
filter_field_->setVisible(false);
connect(filter_toggle, &QToolButton::toggled, filter_field_, [this](bool visible) {
filter_field_->setVisible(visible);
if (visible) filter_field_->setFocus();
});
}
layout->addWidget(header); layout->addWidget(header);
if (filter_field_) {
auto* filter_wrapper = new QWidget(this);
filter_wrapper->setObjectName("panelSectionFilterWrapper");
auto* filter_wrapper_layout = new QVBoxLayout(filter_wrapper);
filter_wrapper_layout->setContentsMargins(style::metrics::section_body_padding,
0,
style::metrics::section_body_padding,
0);
filter_wrapper_layout->setSpacing(0);
filter_wrapper_layout->addWidget(filter_field_);
layout->addWidget(filter_wrapper);
}
connect(toggle, &QToolButton::toggled, this, [this, toggle](bool expanded) { connect(toggle_button_, &QToolButton::toggled, this, [this](bool expanded) {
toggle->setArrowType(expanded ? Qt::DownArrow : Qt::RightArrow); toggle_button_->setArrowType(expanded ? Qt::DownArrow : Qt::RightArrow);
body_->setVisible(expanded); body_->setVisible(expanded);
if (filter_field_) {
auto* wrapper = filter_field_->parentWidget();
if (wrapper) wrapper->setVisible(expanded && filter_field_->isVisible());
}
}); });
} else if (!filter_placeholder.isEmpty()) {
filter_field_ = qobject_cast<QLineEdit*>(makeSectionFilterField(filter_placeholder, this));
auto* filter_wrapper = new QWidget(this);
filter_wrapper->setObjectName("panelSectionFilterWrapper");
auto* filter_wrapper_layout = new QVBoxLayout(filter_wrapper);
filter_wrapper_layout->setContentsMargins(style::metrics::section_body_padding,
0,
style::metrics::section_body_padding,
0);
filter_wrapper_layout->setSpacing(0);
filter_wrapper_layout->addWidget(filter_field_);
layout->addWidget(filter_wrapper);
} }
body_ = new QWidget(this); body_ = new QWidget(this);
@@ -136,4 +77,25 @@ void Section::addBodyWidget(QWidget* widget) {
body_layout_->addWidget(widget); body_layout_->addWidget(widget);
} }
void Section::clearBody() {
while (auto* item = body_layout_->takeAt(0)) {
if (auto* widget = item->widget()) widget->deleteLater();
delete item;
}
}
void Section::addHeaderWidget(QWidget* widget) {
if (!header_layout_) return;
header_layout_->addWidget(widget);
}
bool Section::isExpanded() const {
return !toggle_button_ || toggle_button_->isChecked();
}
void Section::setExpanded(bool expanded) {
if (!toggle_button_) return;
toggle_button_->setChecked(expanded);
}
} // namespace ifcinterface::components } // namespace ifcinterface::components
+8 -3
View File
@@ -23,7 +23,8 @@
#include <QWidget> #include <QWidget>
class QLineEdit; class QHBoxLayout;
class QToolButton;
class QVBoxLayout; class QVBoxLayout;
namespace ifcinterface::components { namespace ifcinterface::components {
@@ -38,15 +39,19 @@ class Section : public QWidget {
public: public:
explicit Section(const QString& title, explicit Section(const QString& title,
SectionHeaderMode header_mode = SectionHeaderMode::Visible, SectionHeaderMode header_mode = SectionHeaderMode::Visible,
const QString& filter_placeholder = {},
QWidget* parent = nullptr); QWidget* parent = nullptr);
void addBodyWidget(QWidget* widget); void addBodyWidget(QWidget* widget);
void clearBody();
void addHeaderWidget(QWidget* widget);
bool isExpanded() const;
void setExpanded(bool expanded);
private: private:
QWidget* body_ = nullptr; QWidget* body_ = nullptr;
QVBoxLayout* body_layout_ = nullptr; QVBoxLayout* body_layout_ = nullptr;
QLineEdit* filter_field_ = nullptr; QHBoxLayout* header_layout_ = nullptr;
QToolButton* toggle_button_ = nullptr;
}; };
} // namespace ifcinterface::components } // namespace ifcinterface::components
+161 -88
View File
@@ -23,41 +23,49 @@
namespace ifcinterface::components::style { namespace ifcinterface::components::style {
QString buildAppStyleSheet() { QString buildAppStyleSheet() {
return QString(R"( QString stylesheet = QStringLiteral(R"(
QMainWindow#appWindow { QMainWindow#appWindow {
background: #26292f; background: ${app_background};
color: %6; color: ${primary_text};
selection-background-color: #39b54a; selection-background-color: ${selection_background};
selection-color: #14161a; selection-color: ${selection_text};
}
QDialog#appDialog {
background: ${app_background};
color: ${primary_text};
} }
QFrame#ribbonShell { QFrame#ribbonShell {
background: #2d3138; background: ${ribbon_shell_background};
border-bottom: 1px solid #1b1d22; border-bottom: 1px solid ${border};
} }
QTabBar::tab { QTabBar::tab {
background: transparent; background: transparent;
color: #8d97a7; color: ${secondary_text};
padding: 8px 14px; padding: 8px 14px;
margin-right: 2px; margin-right: 2px;
border-bottom: 2px solid transparent; border-bottom: 2px solid transparent;
} }
QTabBar::tab:selected { QTabBar::tab:selected {
color: #f2f5fa; color: ${primary_text};
border-bottom: 2px solid #39b54a; border-bottom: 2px solid ${selection_background};
} }
QTabBar::tab:hover { QTabBar::tab:hover {
color: #ffffff; color: ${ribbon_tab_hover_text};
} }
QFrame#ribbonBand { QFrame#ribbonBand {
background: #31353d; background: ${ribbon_band_background};
border-top: 1px solid #3b4048; border-top: 1px solid ${border};
}
QTabWidget::pane {
border: none;
background: transparent;
} }
QFrame#ribbonPage { QFrame#ribbonPage {
background: transparent; background: transparent;
} }
QFrame#ribbonGroup { QFrame#ribbonGroup {
background: transparent; background: transparent;
border-right: 1px solid #434852; border-right: 1px solid ${border};
} }
QLabel#ribbonGroupLabel { QLabel#ribbonGroupLabel {
font-size: 9px; font-size: 9px;
@@ -69,27 +77,27 @@ QString buildAppStyleSheet() {
border: none; border: none;
padding: 6px 4px 4px 4px; padding: 6px 4px 4px 4px;
font-size: 11px; font-size: 11px;
color: #d6dce6; color: ${primary_text};
} }
QToolButton#ribbonButton:hover { QToolButton#ribbonButton:hover {
background: #3a3f48; background: ${ribbon_button_hover};
} }
QToolButton#ribbonButton:pressed { QToolButton#ribbonButton:pressed {
background: #24282f; background: ${ribbon_button_pressed};
} }
QFrame#viewportShell { QFrame#viewportShell {
background: #202329; background: ${viewport_shell_background};
border-top: 1px solid #1d2025; border-top: none;
} }
QFrame#viewportFrame { QFrame#viewportFrame {
background: #1a1d22; background: ${viewport_background};
border: 1px solid #333942; border: 1px solid ${border};
} }
QDockWidget { QDockWidget {
color: #d0d5dd; color: ${primary_text};
} }
QLabel { QLabel {
color: %6; color: ${primary_text};
background: transparent; background: transparent;
} }
QAbstractItemView, QAbstractItemView,
@@ -97,83 +105,127 @@ QString buildAppStyleSheet() {
QListWidget, QListWidget,
QTableWidget, QTableWidget,
QLineEdit, QLineEdit,
QSpinBox,
QDoubleSpinBox,
QCheckBox,
QPushButton,
QToolButton { QToolButton {
color: %6; color: ${primary_text};
} }
QLabel[textRole="secondary"] { QLabel[textRole="secondary"] {
color: %7; color: ${secondary_text};
} }
QLabel[textRole="disabled"] { QLabel[textRole="disabled"] {
color: %8; color: ${disabled_text};
} }
QLabel[textRole="warning"] { QLabel[textRole="warning"] {
color: %9; color: ${warning_text};
} }
QLabel#panelTitleText { QLabel#panelTitleText {
color: #dfe4ec; color: ${primary_text};
font-size: 10px; font-size: 10px;
font-weight: 700; font-weight: 700;
letter-spacing: 0.08em; letter-spacing: 0.08em;
} }
QToolButton#panelTitleButton { QToolButton#panelTitleButton {
color: #8e97a5; color: ${panel_title_button};
border: none; border: none;
background: transparent; background: transparent;
} }
QToolButton#panelTitleButton:hover { QToolButton#panelTitleButton:hover {
color: #ffffff; color: ${ribbon_tab_hover_text};
background: #353a42; background: ${panel_title_button_hover};
} }
QFrame#panel { QFrame#panel {
background: #2b2f36; background: ${panel_background};
border: 1px solid #3e444e; border: 1px solid ${border};
border-radius: %1px; border-radius: ${panel_radius}px;
} }
QTreeWidget, QListWidget, QTableWidget, QAbstractScrollArea { QTreeWidget, QListWidget, QTableWidget, QAbstractScrollArea {
background: #2b2f36; background: ${panel_background};
border: none; border: none;
outline: none; outline: none;
gridline-color: #333842; gridline-color: ${border};
} }
QTreeWidget::viewport, QListWidget::viewport, QTableWidget::viewport { QTreeWidget::viewport, QListWidget::viewport, QTableWidget::viewport {
background: #2b2f36; background: ${panel_background};
} }
QHeaderView::section { QHeaderView::section {
background: #31353d; background: ${control_background};
color: #b5becc; color: ${primary_text};
border: none; border: none;
border-bottom: 1px solid #434a55; border-bottom: 1px solid ${border};
padding: 7px 8px; padding: 7px 8px;
font-weight: 600; font-weight: 600;
} }
QTableCornerButton::section { QTableCornerButton::section {
background: #31353d; background: ${control_background};
border: none; border: none;
} }
QScrollArea { QScrollArea {
background: #2b2f36; background: ${panel_background};
border: none; border: none;
} }
QScrollArea > QWidget > QWidget { QScrollArea > QWidget > QWidget {
background: #2b2f36; background: ${panel_background};
} }
QLineEdit { QLineEdit {
background: #31353d; background: ${control_background};
border: 1px solid #434a55; border: 1px solid ${border};
border-radius: %1px; border-radius: ${panel_radius}px;
padding: %2px %2px; padding: ${padding}px ${padding}px;
color: #d9dfeb; color: ${primary_text};
} }
QLineEdit:focus { QLineEdit:focus {
border: 1px solid #5b6472; border: 1px solid ${control_border_focus};
} }
QFrame#entityClassCard { QSpinBox, QDoubleSpinBox {
background: #26292f; background: ${control_background};
border: 1px solid #404650; border: 1px solid ${border};
border-radius: %1px; border-radius: ${panel_radius}px;
padding: ${padding}px ${padding}px;
}
QSpinBox:focus, QDoubleSpinBox:focus {
border: 1px solid ${control_border_focus};
}
QCheckBox {
background: transparent;
spacing: 8px;
}
QCheckBox::indicator {
width: 16px;
height: 16px;
border: 1px solid ${border};
border-radius: ${panel_radius}px;
background: ${control_background};
}
QCheckBox::indicator:hover {
background: ${ribbon_button_hover};
}
QCheckBox::indicator:checked {
border: 1px solid ${selection_background};
background: ${selection_background};
}
QPushButton {
background: ${control_background};
border: 1px solid ${border};
border-radius: ${panel_radius}px;
padding: ${padding}px ${padding}px;
}
QPushButton:hover {
background: ${ribbon_button_hover};
}
QPushButton:pressed {
background: ${ribbon_button_pressed};
}
QFrame#entityClassBox,
QGroupBox#propertySetBox {
background: ${box_background};
border: 1px solid ${border};
border-radius: ${panel_radius}px;
} }
QLabel#entityClassLabel { QLabel#entityClassLabel {
color: #eef2f8; color: ${primary_text};
font-weight: 700; font-weight: 700;
background: transparent; background: transparent;
} }
@@ -194,13 +246,13 @@ QString buildAppStyleSheet() {
margin: 0 2px 2px 2px; margin: 0 2px 2px 2px;
} }
QScrollBar::handle:vertical, QScrollBar::handle:horizontal { QScrollBar::handle:vertical, QScrollBar::handle:horizontal {
background: #525a67; background: ${scroll_handle};
border-radius: %1px; border-radius: ${panel_radius}px;
min-height: 24px; min-height: 24px;
min-width: 24px; min-width: 24px;
} }
QScrollBar::handle:vertical:hover, QScrollBar::handle:horizontal:hover { QScrollBar::handle:vertical:hover, QScrollBar::handle:horizontal:hover {
background: #697385; background: ${scroll_handle_hover};
} }
QScrollBar::add-line, QScrollBar::sub-line, QScrollBar::add-line, QScrollBar::sub-line,
QScrollBar::add-page, QScrollBar::sub-page { QScrollBar::add-page, QScrollBar::sub-page {
@@ -208,35 +260,29 @@ QString buildAppStyleSheet() {
border: none; border: none;
} }
QStatusBar { QStatusBar {
background: #24272c; background: ${status_background};
border-top: 1px solid #1a1c20;
} }
QStatusBar QLabel { QStatusBar QLabel {
color: %7; color: ${secondary_text};
background: transparent; background: transparent;
border: none; border: none;
padding: 2px 8px; padding: 2px 8px;
} }
QGroupBox { QGroupBox {
background: transparent; background: transparent;
border: 1px solid #404650; border: 1px solid ${border};
border-radius: %1px; border-radius: ${panel_radius}px;
margin-top: 10px; margin-top: 10px;
padding-top: 10px; padding-top: 10px;
} }
QGroupBox#propertySetCard { QGroupBox#propertySetBox::title {
background: #26292f;
border: 1px solid #404650;
border-radius: %1px;
}
QGroupBox#propertySetCard::title {
subcontrol-origin: margin; subcontrol-origin: margin;
left: %2px; left: ${padding}px;
padding: 0 4px; padding: 0 4px;
color: #d5dbe5; color: ${primary_text};
} }
QGroupBox#propertySetCard > QWidget { QGroupBox#propertySetBox > QWidget {
background: #26292f; background: ${box_background};
} }
QWidget#panelSection { QWidget#panelSection {
background: transparent; background: transparent;
@@ -248,22 +294,22 @@ QString buildAppStyleSheet() {
background: transparent; background: transparent;
} }
QWidget#panelScrollBody { QWidget#panelScrollBody {
background: #2b2f36; background: ${panel_background};
} }
QFrame#panelSectionHeader { QFrame#panelSectionHeader {
background: #26292f; background: ${section_header_background};
} }
QToolButton#panelSectionHeaderButton { QToolButton#panelSectionHeaderButton {
background: transparent; background: transparent;
border: none; border: none;
color: #e1e7f0; color: ${primary_text};
font-weight: 700; font-weight: 700;
text-align: left; text-align: left;
padding: %3px; padding: ${section_header_padding}px;
margin: 0; margin: 0;
} }
QToolButton#panelSectionHeaderButton:hover { QToolButton#panelSectionHeaderButton:hover {
color: #ffffff; color: ${ribbon_tab_hover_text};
} }
QToolButton#panelSectionHeaderButton::menu-indicator { QToolButton#panelSectionHeaderButton::menu-indicator {
image: none; image: none;
@@ -272,26 +318,53 @@ QString buildAppStyleSheet() {
QToolButton#panelSectionFilterToggle { QToolButton#panelSectionFilterToggle {
background: transparent; background: transparent;
border: none; border: none;
padding: %3px; padding: ${section_header_padding}px;
} }
QToolButton#panelSectionFilterToggle:hover { QToolButton#panelSectionFilterToggle:hover {
background: #353a42; background: ${panel_title_button_hover};
} }
QWidget#panelSectionBody { QWidget#panelSectionBody {
background: transparent; background: transparent;
} }
QLabel#keyValueValueLabel { QLabel#keyValueValueLabel {
color: #dce2eb; color: ${key_value_value_text};
background: transparent; background: transparent;
} }
)") )");
.arg(metrics::panel_radius)
.arg(metrics::padding) stylesheet.replace("${panel_radius}", QString::number(metrics::panel_radius));
.arg(metrics::section_header_padding) stylesheet.replace("${padding}", QString::number(metrics::padding));
.arg(palette::primary_text) stylesheet.replace("${section_header_padding}", QString::number(metrics::section_header_padding));
.arg(palette::secondary_text)
.arg(palette::disabled_text) stylesheet.replace("${app_background}", palette::app_background);
.arg(palette::warning_text); stylesheet.replace("${border}", palette::border);
stylesheet.replace("${selection_background}", palette::selection_background);
stylesheet.replace("${selection_text}", palette::selection_text);
stylesheet.replace("${ribbon_shell_background}", palette::ribbon_shell_background);
stylesheet.replace("${ribbon_tab_hover_text}", palette::ribbon_tab_hover_text);
stylesheet.replace("${ribbon_band_background}", palette::ribbon_band_background);
stylesheet.replace("${ribbon_button_hover}", palette::ribbon_button_hover);
stylesheet.replace("${ribbon_button_pressed}", palette::ribbon_button_pressed);
stylesheet.replace("${viewport_shell_background}", palette::viewport_shell_background);
stylesheet.replace("${viewport_background}", palette::viewport_background);
stylesheet.replace("${panel_title_button}", palette::panel_title_button);
stylesheet.replace("${panel_title_button_hover}", palette::panel_title_button_hover);
stylesheet.replace("${panel_background}", palette::panel_background);
stylesheet.replace("${control_background}", palette::control_background);
stylesheet.replace("${control_border_focus}", palette::control_border_focus);
stylesheet.replace("${box_background}", palette::box_background);
stylesheet.replace("${scroll_handle}", palette::scroll_handle);
stylesheet.replace("${scroll_handle_hover}", palette::scroll_handle_hover);
stylesheet.replace("${status_background}", palette::status_background);
stylesheet.replace("${section_header_background}", palette::section_header_background);
stylesheet.replace("${key_value_value_text}", palette::key_value_value_text);
stylesheet.replace("${primary_text}", palette::primary_text);
stylesheet.replace("${secondary_text}", palette::secondary_text);
stylesheet.replace("${disabled_text}", palette::disabled_text);
stylesheet.replace("${warning_text}", palette::warning_text);
return stylesheet;
} }
} // namespace ifcinterface::components::style } // namespace ifcinterface::components::style
+23
View File
@@ -34,10 +34,33 @@ inline constexpr int panel_radius = 3;
namespace ifcinterface::components::style::palette { namespace ifcinterface::components::style::palette {
inline constexpr auto app_background = "#26292f";
inline constexpr auto border = "#3e444e";
inline constexpr auto selection_background = "#39b54a";
inline constexpr auto ribbon_shell_background = "#2d3138";
inline constexpr auto ribbon_tab_hover_text = "#ffffff";
inline constexpr auto ribbon_band_background = "#31353d";
inline constexpr auto ribbon_button_hover = "#3a3f48";
inline constexpr auto ribbon_button_pressed = "#24282f";
inline constexpr auto viewport_shell_background = "#202329";
inline constexpr auto viewport_background = "#1a1d22";
inline constexpr auto panel_title_button = "#8e97a5";
inline constexpr auto panel_title_button_hover = "#353a42";
inline constexpr auto panel_background = "#2b2f36";
inline constexpr auto control_background = "#31353d";
inline constexpr auto control_border_focus = "#5b6472";
inline constexpr auto box_background = "#26292f";
inline constexpr auto scroll_handle = "#525a67";
inline constexpr auto scroll_handle_hover = "#697385";
inline constexpr auto status_background = "#26292f";
inline constexpr auto section_header_background = "#26292f";
inline constexpr auto key_value_value_text = "#dce2eb";
inline constexpr auto primary_text = "#d0d5dd"; inline constexpr auto primary_text = "#d0d5dd";
inline constexpr auto secondary_text = "#9aa4b3"; inline constexpr auto secondary_text = "#9aa4b3";
inline constexpr auto disabled_text = "#8f98a6"; inline constexpr auto disabled_text = "#8f98a6";
inline constexpr auto warning_text = "#e4b35a"; inline constexpr auto warning_text = "#e4b35a";
inline constexpr auto selection_text = "#14161a";
} // namespace ifcinterface::components::style::palette } // namespace ifcinterface::components::style::palette
+2 -2
View File
@@ -61,11 +61,11 @@ QIcon makeTintedSvgIcon(const QString& icon_path, const QString& normal,
return icon; return icon;
} }
QIcon makePanelSvgIcon(const QString& icon_path) { QIcon makeSvgIcon(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) { QPixmap makeSvgPixmap(const QString& icon_path, const QSize& size) {
return renderTintedSvgPixmap(icon_path, "#e7ebf2", size); return renderTintedSvgPixmap(icon_path, "#e7ebf2", size);
} }
+2 -2
View File
@@ -33,8 +33,8 @@ QIcon makeTintedSvgIcon(const QString& icon_path,
const QString& normal = "#39b54a", const QString& normal = "#39b54a",
const QString& active = "#53c763", const QString& active = "#53c763",
const QString& disabled = "#6f7988"); const QString& disabled = "#6f7988");
QIcon makePanelSvgIcon(const QString& icon_path); QIcon makeSvgIcon(const QString& icon_path);
QPixmap makePanelSvgPixmap(const QString& icon_path, const QSize& size); QPixmap makeSvgPixmap(const QString& icon_path, const QSize& size);
} // namespace ifcinterface::components::icons } // namespace ifcinterface::components::icons
+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5 12.5L9.5 17L19 7.5" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 227 B

+5
View File
@@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21Z" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9 9L15 15" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M15 9L9 15" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 545 B

+2
View File
@@ -41,5 +41,7 @@
<file alias="cube-dots.svg">icons/cube-dots.svg</file> <file alias="cube-dots.svg">icons/cube-dots.svg</file>
<file alias="cursor-pointer.svg">icons/cursor-pointer.svg</file> <file alias="cursor-pointer.svg">icons/cursor-pointer.svg</file>
<file alias="sidebar-expand.svg">icons/sidebar-expand.svg</file> <file alias="sidebar-expand.svg">icons/sidebar-expand.svg</file>
<file alias="check.svg">icons/check.svg</file>
<file alias="xmark-circle.svg">icons/xmark-circle.svg</file>
</qresource> </qresource>
</RCC> </RCC>
@@ -1,139 +0,0 @@
// 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 "../../components/KeyValueTable.h"
#include "../../components/Section.h"
#include "../../components/SvgIcon.h"
#include <QFrame>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QVBoxLayout>
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* 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) {
rows.append({row.key, row.value, "keyValueValueLabel", "", "", 0});
}
layout->addWidget(new ifcinterface::components::KeyValueTable(rows, group));
return group;
}
QWidget* makeAttributeList(const QList<ifcinterface::panels::properties::KeyValueRow>& rows, QWidget* parent = nullptr) {
QList<ifcinterface::components::KeyValueTableRow> table_rows;
for (const auto& row : rows) {
table_rows.append({row.key, row.value, "keyValueValueLabel", "", "", 0});
}
return new ifcinterface::components::KeyValueTable(table_rows, parent);
}
QWidget* makeRelationshipList(const QList<ifcinterface::panels::properties::RelationshipRow>& rows, QWidget* parent = nullptr) {
QList<ifcinterface::components::KeyValueTableRow> table_rows;
for (const auto& row_data : rows) {
table_rows.append({row_data.key,
row_data.value,
"keyValueValueLabel",
":/icons/cursor-pointer.svg",
"keyValueTrailingIconLabel",
72});
}
return new ifcinterface::components::KeyValueTable(table_rows, parent);
}
} // namespace
namespace ifcinterface::panels::properties {
PropertiesPanelWidget::PropertiesPanelWidget(QWidget* parent)
: QWidget(parent)
{
content_layout_ = new QVBoxLayout(this);
content_layout_->setContentsMargins(0, 0, 0, 0);
content_layout_->setSpacing(12);
}
void PropertiesPanelWidget::render(const PropertiesPanelState& state) {
while (auto* item = content_layout_->takeAt(0)) {
if (auto* widget = item->widget()) widget->deleteLater();
delete item;
}
auto* entity_card = new QFrame(this);
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(components::icons::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->setProperty("textRole", "secondary");
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);
QList<QWidget*> property_set_widgets;
for (const auto& property_set : state.property_sets) {
property_set_widgets.append(makePropertySetPanel(property_set, this));
}
QList<QWidget*> quantity_set_widgets;
for (const auto& property_set : state.quantity_sets) {
quantity_set_widgets.append(makePropertySetPanel(property_set, this));
}
auto* entity_section = new components::Section("", components::SectionHeaderMode::Hidden, "", this);
entity_section->addBodyWidget(entity_card);
auto* attributes_section = new components::Section("Attributes", components::SectionHeaderMode::Visible, "", this);
attributes_section->addBodyWidget(makeAttributeList(state.attributes, this));
auto* relationships_section = new components::Section("Relationships", components::SectionHeaderMode::Visible, "", this);
relationships_section->addBodyWidget(makeRelationshipList(state.relationships, this));
auto* properties_section = new components::Section("Properties", components::SectionHeaderMode::Visible, "Filter properties or sets", this);
for (auto* widget : property_set_widgets) properties_section->addBodyWidget(widget);
auto* quantities_section = new components::Section("Quantities", components::SectionHeaderMode::Visible, "Filter quantities or sets", this);
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);
}
} // namespace ifcinterface::panels::properties
@@ -18,9 +18,9 @@
* * * *
********************************************************************************/ ********************************************************************************/
#include "PropertiesPanelView.h" #include "View.h"
#include "PropertiesPanelWidget.h" #include "Widget.h"
#include "../../ElementRegistry.h" #include "../../ElementRegistry.h"
#include "../../../ifcviewer/AppSettings.h" #include "../../../ifcviewer/AppSettings.h"
@@ -21,7 +21,7 @@
#ifndef IFCINTERFACE_PANELS_PROPERTIESPANELVIEW_H #ifndef IFCINTERFACE_PANELS_PROPERTIESPANELVIEW_H
#define IFCINTERFACE_PANELS_PROPERTIESPANELVIEW_H #define IFCINTERFACE_PANELS_PROPERTIESPANELVIEW_H
#include "PropertiesPanelTypes.h" #include "Types.h"
#include <QObject> #include <QObject>
+219
View File
@@ -0,0 +1,219 @@
// 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 "Widget.h"
#include "../../components/KeyValueTable.h"
#include "../../components/Section.h"
#include "../../components/Style.h"
#include "../../components/SvgIcon.h"
#include <QFrame>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QToolButton>
#include <QVBoxLayout>
namespace {
QWidget* makePropertySetPanel(const ifcinterface::panels::properties::PropertySet& property_set, QWidget* parent = nullptr) {
auto* group = new QGroupBox(property_set.title, parent);
group->setObjectName("propertySetBox");
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) {
rows.append({row.key, row.value, "keyValueValueLabel", "", "", 0});
}
layout->addWidget(new ifcinterface::components::KeyValueTable(rows, group));
return group;
}
QWidget* makeAttributeList(const QList<ifcinterface::panels::properties::KeyValueRow>& rows, QWidget* parent = nullptr) {
QList<ifcinterface::components::KeyValueTableRow> table_rows;
for (const auto& row : rows) {
table_rows.append({row.key, row.value, "keyValueValueLabel", "", "", 0});
}
return new ifcinterface::components::KeyValueTable(table_rows, parent);
}
QWidget* makeRelationshipList(const QList<ifcinterface::panels::properties::RelationshipRow>& rows, QWidget* parent = nullptr) {
QList<ifcinterface::components::KeyValueTableRow> table_rows;
for (const auto& row_data : rows) {
table_rows.append({row_data.key,
row_data.value,
"keyValueValueLabel",
":/icons/cursor-pointer.svg",
"keyValueTrailingIconLabel",
72});
}
return new ifcinterface::components::KeyValueTable(table_rows, parent);
}
QWidget* makeFilterWrapper(QLineEdit** field_out, QWidget* parent = nullptr) {
auto* wrapper = new QWidget(parent);
wrapper->setObjectName("panelSectionFilterWrapper");
auto* layout = new QVBoxLayout(wrapper);
layout->setContentsMargins(ifcinterface::components::style::metrics::section_body_padding,
0,
ifcinterface::components::style::metrics::section_body_padding,
0);
layout->setSpacing(0);
auto* field = new QLineEdit(wrapper);
field->setClearButtonEnabled(true);
field->addAction(ifcinterface::components::icons::makeSvgIcon(":/icons/filter.svg"), QLineEdit::LeadingPosition);
field->setVisible(false);
layout->addWidget(field);
if (field_out) *field_out = field;
return wrapper;
}
} // namespace
namespace ifcinterface::panels::properties {
PropertiesPanelWidget::PropertiesPanelWidget(QWidget* parent)
: QWidget(parent)
{
content_layout_ = new QVBoxLayout(this);
content_layout_->setContentsMargins(0, 0, 0, 0);
content_layout_->setSpacing(12);
auto* entity_box = new QFrame(this);
entity_box->setObjectName("entityClassBox");
auto* entity_layout = new QHBoxLayout(entity_box);
entity_layout->setContentsMargins(10, 8, 10, 8);
entity_layout->setSpacing(10);
auto* entity_icon = new QLabel(entity_box);
entity_icon->setPixmap(components::icons::makeSvgPixmap(":/icons/cube-dots.svg", QSize(28, 28)));
entity_icon->setAlignment(Qt::AlignCenter);
auto* entity_text = new QWidget(entity_box);
auto* entity_text_layout = new QVBoxLayout(entity_text);
entity_text_layout->setContentsMargins(0, 0, 0, 0);
entity_text_layout->setSpacing(2);
entity_class_label_ = new QLabel(entity_text);
entity_class_label_->setObjectName("entityClassLabel");
entity_type_label_ = new QLabel(entity_text);
entity_type_label_->setProperty("textRole", "secondary");
entity_text_layout->addWidget(entity_class_label_);
entity_text_layout->addWidget(entity_type_label_);
entity_layout->addWidget(entity_icon, 0, Qt::AlignVCenter);
entity_layout->addWidget(entity_text, 1, Qt::AlignVCenter);
entity_section_ = new components::Section("", components::SectionHeaderMode::Hidden, this);
entity_section_->addBodyWidget(entity_box);
attributes_section_ = new components::Section("Attributes", components::SectionHeaderMode::Visible, this);
relationships_section_ = new components::Section("Relationships", components::SectionHeaderMode::Visible, this);
properties_section_ = new components::Section("Properties", components::SectionHeaderMode::Visible, this);
quantities_section_ = new components::Section("Quantities", components::SectionHeaderMode::Visible, this);
properties_filter_toggle_ = new QToolButton(properties_section_);
properties_filter_toggle_->setObjectName("panelSectionFilterToggle");
properties_filter_toggle_->setCheckable(true);
properties_filter_toggle_->setIcon(components::icons::makeSvgIcon(":/icons/filter.svg"));
properties_filter_toggle_->setAutoRaise(true);
properties_section_->addHeaderWidget(properties_filter_toggle_);
quantities_filter_toggle_ = new QToolButton(quantities_section_);
quantities_filter_toggle_->setObjectName("panelSectionFilterToggle");
quantities_filter_toggle_->setCheckable(true);
quantities_filter_toggle_->setIcon(components::icons::makeSvgIcon(":/icons/filter.svg"));
quantities_filter_toggle_->setAutoRaise(true);
quantities_section_->addHeaderWidget(quantities_filter_toggle_);
properties_filter_wrapper_ = makeFilterWrapper(&properties_filter_field_, properties_section_);
properties_filter_field_->setPlaceholderText("Filter properties or sets");
quantities_filter_wrapper_ = makeFilterWrapper(&quantities_filter_field_, quantities_section_);
quantities_filter_field_->setPlaceholderText("Filter quantities or sets");
connect(properties_filter_toggle_, &QToolButton::toggled, properties_filter_field_, [this](bool visible) {
properties_filter_field_->setVisible(visible);
properties_filter_wrapper_->setVisible(visible);
if (visible) properties_filter_field_->setFocus();
});
connect(quantities_filter_toggle_, &QToolButton::toggled, quantities_filter_field_, [this](bool visible) {
quantities_filter_field_->setVisible(visible);
quantities_filter_wrapper_->setVisible(visible);
if (visible) quantities_filter_field_->setFocus();
});
properties_filter_wrapper_->setVisible(false);
quantities_filter_wrapper_->setVisible(false);
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);
}
void PropertiesPanelWidget::render(const PropertiesPanelState& state) {
const bool attributes_expanded = attributes_section_->isExpanded();
const bool relationships_expanded = relationships_section_->isExpanded();
const bool properties_expanded = properties_section_->isExpanded();
const bool quantities_expanded = quantities_section_->isExpanded();
const bool properties_filter_visible = properties_filter_toggle_->isChecked();
const bool quantities_filter_visible = quantities_filter_toggle_->isChecked();
const QString properties_filter_text = properties_filter_field_->text();
const QString quantities_filter_text = quantities_filter_field_->text();
entity_class_label_->setText(state.entity.entity_class);
entity_type_label_->setText(state.entity.predefined_type);
attributes_section_->clearBody();
relationships_section_->clearBody();
properties_section_->clearBody();
quantities_section_->clearBody();
QList<QWidget*> property_set_widgets;
for (const auto& property_set : state.property_sets) {
property_set_widgets.append(makePropertySetPanel(property_set, this));
}
QList<QWidget*> quantity_set_widgets;
for (const auto& property_set : state.quantity_sets) {
quantity_set_widgets.append(makePropertySetPanel(property_set, this));
}
attributes_section_->addBodyWidget(makeAttributeList(state.attributes, this));
relationships_section_->addBodyWidget(makeRelationshipList(state.relationships, this));
properties_section_->addBodyWidget(properties_filter_wrapper_);
for (auto* widget : property_set_widgets) properties_section_->addBodyWidget(widget);
quantities_section_->addBodyWidget(quantities_filter_wrapper_);
for (auto* widget : quantity_set_widgets) quantities_section_->addBodyWidget(widget);
attributes_section_->setExpanded(attributes_expanded);
relationships_section_->setExpanded(relationships_expanded);
properties_section_->setExpanded(properties_expanded);
quantities_section_->setExpanded(quantities_expanded);
properties_filter_field_->setText(properties_filter_text);
quantities_filter_field_->setText(quantities_filter_text);
properties_filter_toggle_->setChecked(properties_filter_visible);
quantities_filter_toggle_->setChecked(quantities_filter_visible);
}
} // namespace ifcinterface::panels::properties
@@ -21,11 +21,15 @@
#ifndef IFCINTERFACE_PANELS_PROPERTIESPANELWIDGET_H #ifndef IFCINTERFACE_PANELS_PROPERTIESPANELWIDGET_H
#define IFCINTERFACE_PANELS_PROPERTIESPANELWIDGET_H #define IFCINTERFACE_PANELS_PROPERTIESPANELWIDGET_H
#include "PropertiesPanelTypes.h" #include "Types.h"
#include <QWidget> #include <QWidget>
class QVBoxLayout; class QVBoxLayout;
class QLabel;
class QLineEdit;
class QToolButton;
namespace ifcinterface::components { class Section; }
namespace ifcinterface::panels::properties { namespace ifcinterface::panels::properties {
@@ -38,6 +42,19 @@ public:
private: private:
QVBoxLayout* content_layout_ = nullptr; QVBoxLayout* content_layout_ = nullptr;
QLabel* entity_class_label_ = nullptr;
QLabel* entity_type_label_ = nullptr;
components::Section* entity_section_ = nullptr;
components::Section* attributes_section_ = nullptr;
components::Section* relationships_section_ = nullptr;
components::Section* properties_section_ = nullptr;
components::Section* quantities_section_ = nullptr;
QWidget* properties_filter_wrapper_ = nullptr;
QWidget* quantities_filter_wrapper_ = nullptr;
QLineEdit* properties_filter_field_ = nullptr;
QLineEdit* quantities_filter_field_ = nullptr;
QToolButton* properties_filter_toggle_ = nullptr;
QToolButton* quantities_filter_toggle_ = nullptr;
}; };
} // namespace ifcinterface::panels::properties } // namespace ifcinterface::panels::properties
+222
View File
@@ -0,0 +1,222 @@
// 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 "Dialog.h"
#include "../../../ifcviewer/AppSettings.h"
#include "../../components/Section.h"
#include "../../components/Style.h"
#include "../../components/SvgIcon.h"
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QDoubleSpinBox>
#include <QFrame>
#include <QFormLayout>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QShowEvent>
#include <QSpinBox>
#include <QTabWidget>
#include <QVBoxLayout>
namespace ifcinterface::panels::settings {
SettingsDialog::SettingsDialog(QWidget* parent)
: QDialog(parent)
{
setObjectName("appDialog");
setWindowTitle("Settings");
setModal(true);
resize(520, 420);
setStyleSheet(components::style::buildAppStyleSheet());
setupUi();
}
void SettingsDialog::showEvent(QShowEvent* event) {
syncFromSettings();
QDialog::showEvent(event);
}
void SettingsDialog::setupUi() {
auto* root = new QVBoxLayout(this);
root->setContentsMargins(12, 12, 12, 12);
root->setSpacing(0);
auto* panel = new QFrame(this);
panel->setObjectName("panel");
auto* panel_layout = new QVBoxLayout(panel);
panel_layout->setContentsMargins(0, components::style::metrics::section_body_padding, 0,
components::style::metrics::section_body_padding);
panel_layout->setSpacing(12);
auto* tabs = new QTabWidget(panel);
auto* graphics_tab = new QWidget(tabs);
auto* graphics_layout = new QVBoxLayout(graphics_tab);
graphics_layout->setContentsMargins(0, 0, 0, 0);
graphics_layout->setSpacing(12);
auto* general_section = new components::Section("General", components::SectionHeaderMode::Visible, graphics_tab);
auto* general_body = new QWidget(general_section);
auto* general_form = new QFormLayout(general_body);
general_form->setContentsMargins(0, 0, 0, 0);
general_form->setHorizontalSpacing(16);
general_form->setVerticalSpacing(10);
geometry_library_edit_ = new QLineEdit(general_body);
geometry_library_edit_->setMinimumWidth(300);
general_form->addRow("Geometry Library", geometry_library_edit_);
show_stats_check_ = new QCheckBox(general_body);
general_form->addRow("Show Performance Stats", show_stats_check_);
backface_culling_check_ = new QCheckBox(general_body);
backface_culling_check_->setToolTip(
"Skip triangles facing away from the camera. Big FPS win on closed solids; "
"disable if you see holes in open geometry.");
general_form->addRow("Backface Culling", backface_culling_check_);
general_section->addBodyWidget(general_body);
auto* loading_section = new components::Section("Loading", components::SectionHeaderMode::Visible, graphics_tab);
auto* loading_body = new QWidget(loading_section);
auto* loading_form = new QFormLayout(loading_body);
loading_form->setContentsMargins(0, 0, 0, 0);
loading_form->setHorizontalSpacing(16);
loading_form->setVerticalSpacing(10);
load_data_source_checkbox_ = new QCheckBox(loading_body);
load_data_source_checkbox_->setToolTip(
"Keep the .ifc/.rdb open after loading so element properties can be queried. "
"Disable for geometry-only viewing.");
loading_form->addRow("Load Property Data Source", load_data_source_checkbox_);
apply_coordinate_operation_check_ = new QCheckBox(loading_body);
apply_coordinate_operation_check_->setToolTip(
"Apply each model's IfcCoordinateOperation after load so it lands in "
"georeferenced map coordinates.");
loading_form->addRow("Apply Coordinate Operation", apply_coordinate_operation_check_);
void_limit_spin_ = new QSpinBox(loading_body);
void_limit_spin_->setRange(0, 100000);
loading_form->addRow("Void Limit", void_limit_spin_);
deflection_tolerance_spin_ = new QDoubleSpinBox(loading_body);
deflection_tolerance_spin_->setRange(0.000001, 1000.0);
deflection_tolerance_spin_->setDecimals(6);
deflection_tolerance_spin_->setSingleStep(0.001);
loading_form->addRow("Deflection Tolerance", deflection_tolerance_spin_);
angular_tolerance_spin_ = new QDoubleSpinBox(loading_body);
angular_tolerance_spin_->setRange(0.000001, 3.141592);
angular_tolerance_spin_->setDecimals(6);
angular_tolerance_spin_->setSingleStep(0.05);
loading_form->addRow("Angular Tolerance", angular_tolerance_spin_);
auto* description = new QLabel(
"These settings are shared with the viewer backend and persist via QSettings.",
loading_body);
description->setProperty("textRole", "secondary");
description->setWordWrap(true);
loading_form->addRow(QString(), description);
loading_section->addBodyWidget(loading_body);
graphics_layout->addWidget(general_section);
graphics_layout->addWidget(loading_section);
graphics_layout->addStretch(1);
auto make_placeholder_tab = [tabs](const QString& title, const QString& detail) {
auto* tab = new QWidget(tabs);
auto* layout = new QVBoxLayout(tab);
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(12);
auto* section = new components::Section(title, components::SectionHeaderMode::Visible, tab);
auto* body = new QWidget(section);
auto* body_layout = new QVBoxLayout(body);
body_layout->setContentsMargins(0, 0, 0, 0);
body_layout->setSpacing(8);
auto* heading = new QLabel(title, body);
auto* content = new QLabel(detail, body);
content->setProperty("textRole", "secondary");
content->setWordWrap(true);
body_layout->addWidget(heading);
body_layout->addWidget(content);
section->addBodyWidget(body);
layout->addWidget(section);
layout->addStretch(1);
return tab;
};
tabs->addTab(make_placeholder_tab("Navigation", "Navigation preferences and interaction modes will live here."),
"Navigation");
tabs->addTab(make_placeholder_tab("Keybindings", "Shortcut presets and command bindings will live here."),
"Keybindings");
tabs->addTab(graphics_tab, "Graphics");
tabs->addTab(make_placeholder_tab("Theme", "Theme, density, and UI appearance settings will live here."),
"Theme");
tabs->addTab(make_placeholder_tab("About", "Version, credits, and environment information will live here."),
"About");
auto* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, panel);
if (auto* ok = buttons->button(QDialogButtonBox::Ok)) {
ok->setText("OK");
ok->setIcon(components::icons::makeSvgIcon(":/icons/check.svg"));
}
if (auto* cancel = buttons->button(QDialogButtonBox::Cancel)) {
cancel->setText("Cancel");
cancel->setIcon(components::icons::makeSvgIcon(":/icons/xmark-circle.svg"));
}
connect(buttons, &QDialogButtonBox::accepted, this, &SettingsDialog::onAccepted);
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
panel_layout->addWidget(tabs);
panel_layout->addWidget(buttons);
root->addWidget(panel);
}
void SettingsDialog::syncFromSettings() {
geometry_library_edit_->setText(AppSettings::instance().geometryLibrary());
show_stats_check_->setChecked(AppSettings::instance().showStats());
backface_culling_check_->setChecked(AppSettings::instance().backfaceCulling());
load_data_source_checkbox_->setChecked(AppSettings::instance().loadDataSource());
apply_coordinate_operation_check_->setChecked(AppSettings::instance().applyCoordinateOperation());
void_limit_spin_->setValue(AppSettings::instance().voidLimit());
deflection_tolerance_spin_->setValue(AppSettings::instance().deflectionTolerance());
angular_tolerance_spin_->setValue(AppSettings::instance().angularTolerance());
}
void SettingsDialog::onAccepted() {
AppSettings::instance().setGeometryLibrary(geometry_library_edit_->text());
AppSettings::instance().setShowStats(show_stats_check_->isChecked());
AppSettings::instance().setBackfaceCulling(backface_culling_check_->isChecked());
AppSettings::instance().setLoadDataSource(load_data_source_checkbox_->isChecked());
AppSettings::instance().setApplyCoordinateOperation(apply_coordinate_operation_check_->isChecked());
AppSettings::instance().setVoidLimit(void_limit_spin_->value());
AppSettings::instance().setDeflectionTolerance(deflection_tolerance_spin_->value());
AppSettings::instance().setAngularTolerance(angular_tolerance_spin_->value());
accept();
}
} // namespace ifcinterface::panels::settings
+59
View File
@@ -0,0 +1,59 @@
// 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_SETTINGSDIALOG_H
#define IFCINTERFACE_PANELS_SETTINGSDIALOG_H
#include <QDialog>
class QCheckBox;
class QDoubleSpinBox;
class QLineEdit;
class QShowEvent;
class QSpinBox;
namespace ifcinterface::panels::settings {
class SettingsDialog : public QDialog {
Q_OBJECT
public:
explicit SettingsDialog(QWidget* parent = nullptr);
protected:
void showEvent(QShowEvent* event) override;
private:
void setupUi();
void syncFromSettings();
void onAccepted();
QLineEdit* geometry_library_edit_ = nullptr;
QCheckBox* show_stats_check_ = nullptr;
QCheckBox* backface_culling_check_ = nullptr;
QCheckBox* load_data_source_checkbox_ = nullptr;
QCheckBox* apply_coordinate_operation_check_ = nullptr;
QSpinBox* void_limit_spin_ = nullptr;
QDoubleSpinBox* deflection_tolerance_spin_ = nullptr;
QDoubleSpinBox* angular_tolerance_spin_ = nullptr;
};
} // namespace ifcinterface::panels::settings
#endif
@@ -18,9 +18,9 @@
* * * *
********************************************************************************/ ********************************************************************************/
#include "SpatialHierarchyPanelView.h" #include "View.h"
#include "SpatialHierarchyPanelWidget.h" #include "Widget.h"
namespace ifcinterface::panels::spatial_hierarchy { namespace ifcinterface::panels::spatial_hierarchy {
@@ -21,7 +21,7 @@
#ifndef IFCINTERFACE_PANELS_SPATIALHIERARCHYPANELVIEW_H #ifndef IFCINTERFACE_PANELS_SPATIALHIERARCHYPANELVIEW_H
#define IFCINTERFACE_PANELS_SPATIALHIERARCHYPANELVIEW_H #define IFCINTERFACE_PANELS_SPATIALHIERARCHYPANELVIEW_H
#include "SpatialHierarchyPanelTypes.h" #include "Types.h"
#include <QObject> #include <QObject>
@@ -18,7 +18,7 @@
* * * *
********************************************************************************/ ********************************************************************************/
#include "SpatialHierarchyPanelWidget.h" #include "Widget.h"
#include "../../components/Section.h" #include "../../components/Section.h"
#include "../../components/SvgIcon.h" #include "../../components/SvgIcon.h"
@@ -37,7 +37,7 @@ SpatialHierarchyPanelWidget::SpatialHierarchyPanelWidget(QWidget* parent)
layout->setContentsMargins(0, 0, 0, 0); layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(0); layout->setSpacing(0);
auto* section = new components::Section("", components::SectionHeaderMode::Hidden, "", this); auto* section = new components::Section("", components::SectionHeaderMode::Hidden, this);
tree_ = new QTreeWidget(section); tree_ = new QTreeWidget(section);
tree_->setColumnCount(2); tree_->setColumnCount(2);
@@ -71,8 +71,8 @@ void SpatialHierarchyPanelWidget::addNode(QTreeWidgetItem* parent, const TreeNod
auto* item = new QTreeWidgetItem(parent, {node.name, ""}); auto* item = new QTreeWidgetItem(parent, {node.name, ""});
item->setData(1, Qt::UserRole, node.visible); item->setData(1, Qt::UserRole, node.visible);
item->setSizeHint(0, QSize(0, 24)); item->setSizeHint(0, QSize(0, 24));
item->setIcon(0, components::icons::makePanelSvgIcon(iconPath(node.kind))); item->setIcon(0, components::icons::makeSvgIcon(iconPath(node.kind)));
item->setIcon(1, components::icons::makePanelSvgIcon(node.visible ? ":/icons/eye.svg" : ":/icons/eye-closed.svg")); item->setIcon(1, components::icons::makeSvgIcon(node.visible ? ":/icons/eye.svg" : ":/icons/eye-closed.svg"));
for (const auto& child : node.children) { for (const auto& child : node.children) {
addNode(item, child); addNode(item, child);
} }
@@ -21,7 +21,7 @@
#ifndef IFCINTERFACE_PANELS_SPATIALHIERARCHYPANELWIDGET_H #ifndef IFCINTERFACE_PANELS_SPATIALHIERARCHYPANELWIDGET_H
#define IFCINTERFACE_PANELS_SPATIALHIERARCHYPANELWIDGET_H #define IFCINTERFACE_PANELS_SPATIALHIERARCHYPANELWIDGET_H
#include "SpatialHierarchyPanelTypes.h" #include "Types.h"
#include <QWidget> #include <QWidget>
@@ -18,7 +18,7 @@
* * * *
********************************************************************************/ ********************************************************************************/
#include "TodoPanelWidget.h" #include "Widget.h"
#include "../../components/Section.h" #include "../../components/Section.h"
@@ -34,7 +34,7 @@ TodoPanelWidget::TodoPanelWidget(const QString& title, QWidget* parent)
layout->setContentsMargins(0, 0, 0, 0); layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(0); layout->setSpacing(0);
auto* section = new components::Section("", components::SectionHeaderMode::Hidden, "", this); auto* section = new components::Section("", components::SectionHeaderMode::Hidden, this);
auto* body = new QWidget(section); auto* body = new QWidget(section);
auto* body_layout = new QVBoxLayout(body); auto* body_layout = new QVBoxLayout(body);