add context panel

This commit is contained in:
AndreaRigoni
2026-03-21 20:14:29 +00:00
parent add9d37aea
commit a8f786d8d1
14 changed files with 381 additions and 17 deletions

View File

@@ -7,6 +7,12 @@ add_executable(gcompose
src/ViewportPane.cpp
src/MainPanel.h
src/MainPanel.cpp
src/ContextPanel.h
src/ContextPanel.cpp
src/ContextModel.h
src/ContextModel.cpp
src/StyleManager.h
src/StyleManager.cpp
)
set_target_properties(gcompose PROPERTIES

View File

@@ -0,0 +1,132 @@
#include "ContextModel.h"
#include <QString>
#include <typeinfo>
#include <cxxabi.h>
#include <functional>
ContextModel::ContextModel(QObject* parent)
: QAbstractItemModel(parent), m_rootContext(nullptr) {}
ContextModel::~ContextModel() {}
void ContextModel::setContext(uLib::ObjectsContext* context) {
beginResetModel();
m_rootContext = context;
endResetModel();
}
QModelIndex ContextModel::index(int row, int column, const QModelIndex& parent) const {
if (!hasIndex(row, column, parent) || !m_rootContext) {
return QModelIndex();
}
if (!parent.isValid()) {
if (row < m_rootContext->GetCount()) {
return createIndex(row, column, m_rootContext->GetObject(row));
}
} else {
uLib::Object* parentObj = static_cast<uLib::Object*>(parent.internalPointer());
uLib::ObjectsContext* parentCtx = dynamic_cast<uLib::ObjectsContext*>(parentObj);
if (parentCtx && row < parentCtx->GetCount()) {
return createIndex(row, column, parentCtx->GetObject(row));
}
}
return QModelIndex();
}
QModelIndex ContextModel::parent(const QModelIndex& child) const {
if (!child.isValid() || !m_rootContext) {
return QModelIndex();
}
uLib::Object* childObj = static_cast<uLib::Object*>(child.internalPointer());
// Finding the parent of childObj is O(N) since there is no parent pointer.
// We just do a recursive search starting from root context.
std::function<uLib::ObjectsContext*(uLib::ObjectsContext*, uLib::Object*)> findParent =
[&findParent](uLib::ObjectsContext* ctx, uLib::Object* target) -> uLib::ObjectsContext* {
for (const auto& obj : ctx->GetObjects()) {
if (obj == target) return ctx;
if (auto subCtx = dynamic_cast<uLib::ObjectsContext*>(obj)) {
if (auto p = findParent(subCtx, target)) return p;
}
}
return nullptr;
};
uLib::ObjectsContext* parentCtx = findParent(m_rootContext, childObj);
if (!parentCtx || parentCtx == m_rootContext) {
return QModelIndex(); // Root items have invalid parent index
}
// Now need to find the row of parentCtx in its own parent Context.
uLib::ObjectsContext* grandParentCtx = findParent(m_rootContext, parentCtx);
if (!grandParentCtx) grandParentCtx = m_rootContext;
int row = -1;
for (size_t i = 0; i < grandParentCtx->GetCount(); ++i) {
if (grandParentCtx->GetObject(i) == parentCtx) {
row = (int)i;
break;
}
}
if (row != -1) {
return createIndex(row, 0, parentCtx);
}
return QModelIndex();
}
int ContextModel::rowCount(const QModelIndex& parent) const {
if (!m_rootContext) return 0;
if (!parent.isValid()) {
return m_rootContext->GetCount();
}
uLib::Object* parentObj = static_cast<uLib::Object*>(parent.internalPointer());
if (auto parentCtx = dynamic_cast<uLib::ObjectsContext*>(parentObj)) {
return parentCtx->GetCount();
}
return 0; // leaf node
}
int ContextModel::columnCount(const QModelIndex& parent) const {
return 1;
}
static QString getDemangledName(const std::type_info& info) {
int status = -4;
char* demangled = abi::__cxa_demangle(info.name(), nullptr, nullptr, &status);
QString res = (status == 0 && demangled) ? QString::fromUtf8(demangled) : QString::fromUtf8(info.name());
if (demangled) free(demangled);
// Remove namespaces
int lastColon = res.lastIndexOf("::");
if (lastColon != -1) {
res = res.mid(lastColon + 2);
}
// Remove "class " prefix if any
if (res.startsWith("class ")) res = res.mid(6);
return res;
}
QVariant ContextModel::data(const QModelIndex& index, int role) const {
if (!index.isValid()) return QVariant();
uLib::Object* obj = static_cast<uLib::Object*>(index.internalPointer());
if (role == Qt::DisplayRole) {
return getDemangledName(typeid(*obj));
}
return QVariant();
}
QVariant ContextModel::headerData(int section, Qt::Orientation orientation, int role) const {
if (orientation == Qt::Horizontal && role == Qt::DisplayRole && section == 0) {
return "Object Type";
}
return QVariant();
}

View File

@@ -0,0 +1,26 @@
#ifndef CONTEXT_MODEL_H
#define CONTEXT_MODEL_H
#include <QAbstractItemModel>
#include "Core/ObjectsContext.h"
class ContextModel : public QAbstractItemModel {
Q_OBJECT
public:
explicit ContextModel(QObject* parent = nullptr);
virtual ~ContextModel();
void setContext(uLib::ObjectsContext* context);
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex& child) const override;
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
private:
uLib::ObjectsContext* m_rootContext;
};
#endif // CONTEXT_MODEL_H

View File

@@ -0,0 +1,43 @@
#include "ContextPanel.h"
#include "ContextModel.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QTreeView>
ContextPanel::ContextPanel(QWidget* parent) : QWidget(parent) {
m_layout = new QVBoxLayout(this);
m_layout->setContentsMargins(0, 0, 0, 0);
m_layout->setSpacing(0);
// Title bar setup
m_titleBar = new QWidget(this);
m_titleBar->setObjectName("PaneTitleBar");
m_titleBar->setFixedHeight(22);
auto* titleLayout = new QHBoxLayout(m_titleBar);
titleLayout->setContentsMargins(5, 0, 5, 0);
m_titleLabel = new QLabel("Context Panel", m_titleBar);
m_titleLabel->setObjectName("TitleLabel");
titleLayout->addWidget(m_titleLabel);
titleLayout->addStretch();
m_layout->addWidget(m_titleBar);
m_treeView = new QTreeView(this);
m_treeView->setObjectName("ContextTree");
m_treeView->setHeaderHidden(false);
m_model = new ContextModel(this);
m_treeView->setModel(m_model);
m_layout->addWidget(m_treeView);
}
ContextPanel::~ContextPanel() {}
void ContextPanel::setContext(uLib::ObjectsContext* context) {
m_model->setContext(context);
m_treeView->expandAll();
}

View File

@@ -0,0 +1,31 @@
#ifndef CONTEXT_PANEL_H
#define CONTEXT_PANEL_H
#include <QWidget>
class QTreeView;
class QVBoxLayout;
class QLabel;
class ContextModel;
namespace uLib {
class ObjectsContext;
}
class ContextPanel : public QWidget {
Q_OBJECT
public:
explicit ContextPanel(QWidget* parent = nullptr);
virtual ~ContextPanel();
void setContext(uLib::ObjectsContext* context);
private:
QVBoxLayout* m_layout;
QWidget* m_titleBar;
QLabel* m_titleLabel;
QTreeView* m_treeView;
ContextModel* m_model;
};
#endif // CONTEXT_PANEL_H

View File

@@ -1,10 +1,15 @@
#include "MainPanel.h"
#include "ViewportPane.h"
#include "ContextPanel.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QSplitter>
#include <QLabel>
#include <QPushButton>
#include <QMenu>
#include <QAction>
#include <QApplication>
#include "StyleManager.h"
MainPanel::MainPanel(QWidget* parent) : QWidget(parent) {
auto* mainLayout = new QVBoxLayout(this);
@@ -13,37 +18,72 @@ MainPanel::MainPanel(QWidget* parent) : QWidget(parent) {
// 1. Top Menu Panel
auto* menuPanel = new QWidget(this);
menuPanel->setObjectName("MenuPanel");
menuPanel->setFixedHeight(36);
menuPanel->setStyleSheet("background-color: #2b2b2b; border-bottom: 1px solid #111;");
auto* menuLayout = new QHBoxLayout(menuPanel);
menuLayout->setContentsMargins(10, 0, 10, 0);
menuLayout->setSpacing(15);
auto* logo = new QLabel("G-COMPOSE", menuPanel);
logo->setStyleSheet("font-weight: bold; color: #0078d7; font-size: 14px; letter-spacing: 1px;");
logo->setObjectName("LogoLabel");
auto* btnOpen = new QPushButton("Open", menuPanel);
auto* btnSave = new QPushButton("Save", menuPanel);
QString btnStyle = "QPushButton { background: transparent; color: #ccc; border: none; padding: 5px 10px; }"
"QPushButton:hover { background: #3c3c3c; color: white; border-radius: 4px; }";
btnOpen->setStyleSheet(btnStyle);
btnSave->setStyleSheet(btnStyle);
// File Menu Button
auto* btnFile = new QPushButton("File", menuPanel);
btnFile->setObjectName("MenuButton");
auto* fileMenu = new QMenu(btnFile);
fileMenu->addAction("Open", this, &MainPanel::onOpen);
fileMenu->addAction("Save", this, &MainPanel::onSave);
btnFile->setMenu(fileMenu);
// Theme Menu Button
auto* btnTheme = new QPushButton("Theme", menuPanel);
btnTheme->setObjectName("MenuButton");
auto* themeMenu = new QMenu(btnTheme);
themeMenu->addAction("Dark", this, &MainPanel::onDarkTheme);
themeMenu->addAction("Bright", this, &MainPanel::onBrightTheme);
btnTheme->setMenu(themeMenu);
menuLayout->addWidget(logo);
menuLayout->addWidget(btnOpen);
menuLayout->addWidget(btnSave);
menuLayout->addWidget(btnFile);
menuLayout->addWidget(btnTheme);
menuLayout->addStretch();
mainLayout->addWidget(menuPanel);
// 2. Central Splitter Area
m_rootSplitter = new QSplitter(Qt::Horizontal, this);
m_contextPanel = new ContextPanel(m_rootSplitter);
m_rootSplitter->addWidget(m_contextPanel);
m_firstPane = new ViewportPane(m_rootSplitter);
m_rootSplitter->addWidget(m_firstPane);
// Set initial sizes
QList<int> sizes;
sizes << 200 << 1000;
m_rootSplitter->setSizes(sizes);
mainLayout->addWidget(m_rootSplitter, 1);
}
void MainPanel::setContext(uLib::ObjectsContext* context) {
m_contextPanel->setContext(context);
}
void MainPanel::onOpen() {
// Placeholder for open logic
}
void MainPanel::onSave() {
// Placeholder for save logic
}
void MainPanel::onDarkTheme() {
StyleManager::applyStyle(qApp, "dark");
}
void MainPanel::onBrightTheme() {
StyleManager::applyStyle(qApp, "bright");
}
MainPanel::~MainPanel() {}

View File

@@ -5,6 +5,11 @@
class QSplitter;
class ViewportPane;
class ContextPanel;
namespace uLib {
class ObjectsContext;
}
class MainPanel : public QWidget {
Q_OBJECT
@@ -12,11 +17,19 @@ public:
explicit MainPanel(QWidget* parent = nullptr);
virtual ~MainPanel();
void setContext(uLib::ObjectsContext* context);
ViewportPane* getFirstPane() const { return m_firstPane; }
private slots:
void onOpen();
void onSave();
void onDarkTheme();
void onBrightTheme();
private:
QSplitter* m_rootSplitter;
ViewportPane* m_firstPane;
ContextPanel* m_contextPanel;
};
#endif // MAINPANEL_H

View File

@@ -1,6 +1,7 @@
#include "MainWindow.h"
#include <QSplitter>
#include "MainPanel.h"
#include "Core/ObjectsContext.h"
using namespace uLib;
@@ -14,3 +15,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) {
MainWindow::~MainWindow() {
}
void MainWindow::setContext(uLib::ObjectsContext* context) {
m_panel->setContext(context);
}

View File

@@ -10,6 +10,7 @@ class ViewportPane;
namespace uLib {
namespace Vtk {
}
class ObjectsContext;
}
class MainWindow : public QMainWindow {
@@ -18,6 +19,7 @@ public:
MainWindow(QWidget* parent = nullptr);
virtual ~MainWindow();
void setContext(uLib::ObjectsContext* context);
MainPanel* getPanel() { return m_panel; }
private:

View File

@@ -17,19 +17,19 @@ QViewportPane::QViewportPane(QWidget* parent) : QWidget(parent), m_viewport(null
// Title bar setup
m_titleBar = new QWidget(this);
m_titleBar->setObjectName("PaneTitleBar");
m_titleBar->setFixedHeight(22);
m_titleBar->setStyleSheet("background-color: #333; color: white;");
auto* titleLayout = new QHBoxLayout(m_titleBar);
titleLayout->setContentsMargins(5, 0, 5, 0);
m_titleLabel = new QLabel("Viewport", m_titleBar);
m_titleLabel->setObjectName("TitleLabel");
auto* closeBtn = new QToolButton(m_titleBar);
closeBtn->setObjectName("PaneCloseButton");
closeBtn->setText("X");
closeBtn->setFixedSize(18, 18);
closeBtn->setStyleSheet("QToolButton { border: none; font-weight: bold; background: transparent; color: #ccc; } "
"QToolButton:hover { color: white; background: red; }");
titleLayout->addWidget(m_titleLabel);
titleLayout->addStretch();

View File

@@ -0,0 +1,44 @@
#include "StyleManager.h"
#include <QApplication>
static const QString DARK_THEME = R"(
QWidget#MenuPanel { background-color: #2b2b2b; border-bottom: 1px solid #111; }
QLabel#LogoLabel { font-weight: bold; color: #0078d7; font-size: 14px; letter-spacing: 1px; }
QPushButton#MenuButton { background: transparent; color: #ccc; border: none; padding: 5px 10px; }
QPushButton#MenuButton:hover { background: #3c3c3c; color: white; border-radius: 4px; }
QWidget#PaneTitleBar { background-color: #333; color: white; }
QToolButton#PaneCloseButton { border: none; font-weight: bold; background: transparent; color: #ccc; }
QToolButton#PaneCloseButton:hover { color: white; background: red; }
QMenu { background-color: #2b2b2b; color: white; border: 1px solid #111; }
QMenu::item:selected { background-color: #3c3c3c; }
QTreeView#ContextTree { background-color: #1e1e1e; color: #ccc; border: none; }
QTreeView#ContextTree::item:hover { background-color: #2a2d2e; }
QTreeView#ContextTree::item:selected { background-color: #094771; color: white; }
QHeaderView::section { background-color: #252526; color: #ccc; border: 1px solid #323233; padding: 4px; }
)";
static const QString BRIGHT_THEME = R"(
QWidget#MenuPanel { background-color: #f0f0f0; border-bottom: 1px solid #ccc; }
QLabel#LogoLabel { font-weight: bold; color: #005a9e; font-size: 14px; letter-spacing: 1px; }
QPushButton#MenuButton { background: transparent; color: #333; border: none; padding: 5px 10px; }
QPushButton#MenuButton:hover { background: #d0d0d0; color: black; border-radius: 4px; }
QWidget#PaneTitleBar { background-color: #e0e0e0; color: black; }
QToolButton#PaneCloseButton { border: none; font-weight: bold; background: transparent; color: #666; }
QToolButton#PaneCloseButton:hover { color: white; background: #e81123; }
QMenu { background-color: #f0f0f0; color: black; border: 1px solid #ccc; }
QMenu::item:selected { background-color: #d0d0d0; }
QTreeView#ContextTree { background-color: #ffffff; color: #333; border: none; }
QTreeView#ContextTree::item:hover { background-color: #f2f2f2; }
QTreeView#ContextTree::item:selected { background-color: #0078d7; color: white; }
QHeaderView::section { background-color: #f3f3f3; color: #333; border: 1px solid #ccc; padding: 4px; }
)";
void StyleManager::applyStyle(QApplication* app, const QString& themeName) {
if (!app) return;
if (themeName == "bright") {
app->setStyleSheet(BRIGHT_THEME);
} else {
app->setStyleSheet(DARK_THEME); // default
}
}

View File

@@ -0,0 +1,13 @@
#ifndef STYLEMANAGER_H
#define STYLEMANAGER_H
#include <QString>
class QApplication;
class StyleManager {
public:
static void applyStyle(QApplication* app, const QString& themeName);
};
#endif // STYLEMANAGER_H

View File

@@ -17,19 +17,19 @@ ViewportPane::ViewportPane(QWidget* parent) : QWidget(parent), m_viewport(nullpt
// Title bar setup
m_titleBar = new QWidget(this);
m_titleBar->setObjectName("PaneTitleBar");
m_titleBar->setFixedHeight(22);
m_titleBar->setStyleSheet("background-color: #333; color: white;");
auto* titleLayout = new QHBoxLayout(m_titleBar);
titleLayout->setContentsMargins(5, 0, 5, 0);
m_titleLabel = new QLabel("Viewport", m_titleBar);
m_titleLabel->setObjectName("TitleLabel");
auto* closeBtn = new QToolButton(m_titleBar);
closeBtn->setObjectName("PaneCloseButton");
closeBtn->setText("X");
closeBtn->setFixedSize(18, 18);
closeBtn->setStyleSheet("QToolButton { border: none; font-weight: bold; background: transparent; color: #ccc; } "
"QToolButton:hover { color: white; background: red; }");
titleLayout->addWidget(m_titleLabel);
titleLayout->addStretch();

View File

@@ -2,6 +2,7 @@
#include "MainWindow.h"
#include "MainPanel.h"
#include "ViewportPane.h"
#include "StyleManager.h"
#include "Math/ContainerBox.h"
#include <HEP/Geant/Scene.h>
@@ -11,6 +12,8 @@
#include <Vtk/vtkContainerBox.h>
#include <Vtk/vtkQViewport.h>
#include "Core/ObjectsContext.h"
#include <vtkSmartPointer.h>
#include <vtkCubeSource.h>
#include <vtkPolyDataMapper.h>
@@ -26,6 +29,7 @@ using namespace uLib::literals;
int main(int argc, char** argv) {
QApplication app(argc, argv);
StyleManager::applyStyle(&app, "dark");
std::cout << "Starting gcompose Qt application..." << std::endl;
ContainerBox world_box(Vector3f(1, 1, 1));
@@ -35,8 +39,13 @@ int main(int argc, char** argv) {
scene.ConstructWorldBox(world_box.GetSize(), "G4_AIR");
scene.Initialize();
uLib::ObjectsContext globalContext;
globalContext.AddObject(&world_box);
globalContext.AddObject(&scene);
// 2. Initialize MainWindow (contains embedded VTK QViewport)
MainWindow window;
window.setContext(&globalContext);
MainPanel* panel = window.getPanel();
ViewportPane* pane = panel->getFirstPane();
Vtk::QViewport* viewport = qobject_cast<Vtk::QViewport*>(pane->currentViewport());