232 lines
7.7 KiB
C++
232 lines
7.7 KiB
C++
#include "QViewportPane.h"
|
|
#include <Vtk/vtkQViewport.h>
|
|
#include <Root/QCanvas.h>
|
|
#include <QVBoxLayout>
|
|
#include <QHBoxLayout>
|
|
#include <QLabel>
|
|
#include <QToolButton>
|
|
#include <QMenu>
|
|
#include <QAction>
|
|
#include <QSplitter>
|
|
#include <vtkCamera.h>
|
|
#include "PropertyWidgets.h"
|
|
#include <QSignalBlocker>
|
|
|
|
QViewportPane::QViewportPane(QWidget* parent) : QWidget(parent), m_viewport(nullptr) {
|
|
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, 0, 0);
|
|
|
|
m_titleLabel = new QLabel("Viewport", m_titleBar);
|
|
m_titleLabel->setObjectName("TitleLabel");
|
|
|
|
m_toggleBtn = new QPushButton("Display", m_titleBar);
|
|
m_toggleBtn->setCheckable(true);
|
|
m_toggleBtn->setFixedSize(60, 18);
|
|
m_toggleBtn->setObjectName("DisplayToggleBtn");
|
|
|
|
auto* closeBtn = new QToolButton(m_titleBar);
|
|
closeBtn->setObjectName("PaneCloseButton");
|
|
closeBtn->setText("X");
|
|
closeBtn->setFixedSize(18, 18);
|
|
|
|
titleLayout->addWidget(m_titleLabel);
|
|
titleLayout->addStretch();
|
|
titleLayout->addWidget(m_toggleBtn);
|
|
titleLayout->addSpacing(5);
|
|
titleLayout->addWidget(closeBtn);
|
|
|
|
m_layout->addWidget(m_titleBar);
|
|
|
|
// Main horizontal container for viewport and display panel
|
|
QWidget* mainArea = new QWidget(this);
|
|
QHBoxLayout* hLayout = new QHBoxLayout(mainArea);
|
|
hLayout->setContentsMargins(0, 0, 0, 0);
|
|
hLayout->setSpacing(0);
|
|
m_layout->addWidget(mainArea);
|
|
|
|
// Viewport will be added here via setViewport
|
|
m_viewport = new uLib::Vtk::QViewport(mainArea);
|
|
hLayout->addWidget(m_viewport);
|
|
|
|
// Display Panel (Overlay/Slide-out)
|
|
m_displayPanel = new QFrame(mainArea);
|
|
m_displayPanel->setObjectName("DisplayPropertiesPanel");
|
|
m_displayPanel->setFixedWidth(250);
|
|
m_displayPanel->hide();
|
|
|
|
QVBoxLayout* panelLayout = new QVBoxLayout(m_displayPanel);
|
|
panelLayout->setContentsMargins(5, 5, 5, 5);
|
|
|
|
QLabel* panelHeader = new QLabel("Display Properties", m_displayPanel);
|
|
panelHeader->setStyleSheet("font-weight: bold; padding: 5px;");
|
|
panelLayout->addWidget(panelHeader);
|
|
|
|
m_displayEditor = new uLib::Qt::PropertyEditor(m_displayPanel);
|
|
panelLayout->addWidget(m_displayEditor);
|
|
|
|
hLayout->addWidget(m_displayPanel);
|
|
|
|
connect(m_toggleBtn, &QPushButton::toggled, this, &QViewportPane::toggleDisplayPanel);
|
|
connect(m_titleBar, &QWidget::customContextMenuRequested, this, &QViewportPane::showContextMenu);
|
|
connect(closeBtn, &QToolButton::clicked, this, &QViewportPane::onCloseRequested);
|
|
|
|
m_titleBar->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
}
|
|
|
|
void QViewportPane::toggleDisplayPanel() {
|
|
m_displayPanel->setVisible(m_toggleBtn->isChecked());
|
|
}
|
|
|
|
void QViewportPane::setObject(uLib::Object* obj) {
|
|
m_displayEditor->setObject(obj, true);
|
|
// Auto-show panel if it's a puppet and we want to highlight this feature?
|
|
// User asked for "hiding panel", so maybe we don't auto-show.
|
|
}
|
|
|
|
void QViewportPane::setViewport(QWidget* viewport, const QString& title) {
|
|
if (m_viewport) {
|
|
m_viewport->parentWidget()->layout()->removeWidget(m_viewport);
|
|
delete m_viewport;
|
|
}
|
|
m_viewport = viewport;
|
|
m_titleLabel->setText(title);
|
|
|
|
m_viewport->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
|
auto* mainAreaLayout = static_cast<QHBoxLayout*>(m_displayPanel->parentWidget()->layout());
|
|
mainAreaLayout->insertWidget(0, m_viewport);
|
|
}
|
|
|
|
|
|
QViewportPane::~QViewportPane() {}
|
|
|
|
void QViewportPane::setViewport(QWidget* viewport, const QString& title) {
|
|
if (m_viewport) {
|
|
m_layout->removeWidget(m_viewport);
|
|
delete m_viewport;
|
|
}
|
|
m_viewport = viewport;
|
|
m_titleLabel->setText(title);
|
|
|
|
m_viewport->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
|
m_layout->addWidget(m_viewport);
|
|
}
|
|
|
|
void QViewportPane::addVtkViewport() {
|
|
auto* viewport = new uLib::Vtk::QViewport(this);
|
|
setViewport(viewport, "VTK Viewport");
|
|
}
|
|
|
|
void QViewportPane::addRootCanvas() {
|
|
auto* canvas = new uLib::Root::QCanvas(this);
|
|
setViewport(canvas, "ROOT Canvas");
|
|
}
|
|
|
|
void QViewportPane::onCloseRequested() {
|
|
QSplitter* parentSplitter = qobject_cast<QSplitter*>(parentWidget());
|
|
if (parentSplitter && parentSplitter->count() > 1) {
|
|
deleteLater();
|
|
} else {
|
|
// Can't close the last viewport in the splitter safely. Re-initialize to default VTK canvas.
|
|
addVtkViewport();
|
|
}
|
|
}
|
|
|
|
void QViewportPane::showContextMenu(const QPoint& pos) {
|
|
QMenu menu(this);
|
|
QAction* hSplit = menu.addAction("H split");
|
|
QAction* vSplit = menu.addAction("V split");
|
|
menu.addSeparator();
|
|
|
|
bool isVtk = (qobject_cast<uLib::Vtk::QViewport*>(m_viewport) != nullptr);
|
|
|
|
QAction* changeType = menu.addAction(isVtk ? "Change to ROOT Canvas" : "Change to VTK Viewport");
|
|
|
|
QAction* selected = menu.exec(m_titleBar->mapToGlobal(pos));
|
|
|
|
if (selected == hSplit) {
|
|
AttemptSplit(Qt::Horizontal);
|
|
} else if (selected == vSplit) {
|
|
AttemptSplit(Qt::Vertical);
|
|
} else if (selected == changeType) {
|
|
if (isVtk) {
|
|
addRootCanvas();
|
|
} else {
|
|
addVtkViewport();
|
|
}
|
|
}
|
|
}
|
|
|
|
void QViewportPane::AttemptSplit(Qt::Orientation orientation) {
|
|
QWidget* p = parentWidget();
|
|
if (!p) return;
|
|
|
|
QSplitter* parentSplitter = qobject_cast<QSplitter*>(p);
|
|
if (!parentSplitter) return;
|
|
|
|
QViewportPane* newPane = new QViewportPane();
|
|
|
|
// 1. Synchronize viewport content and camera (VTK Viewport only for now)
|
|
auto* currentVtk = qobject_cast<uLib::Vtk::QViewport*>(m_viewport);
|
|
if (currentVtk) {
|
|
auto* newVtk = qobject_cast<uLib::Vtk::QViewport*>(newPane->currentViewport());
|
|
if (newVtk) {
|
|
// Copy puppets
|
|
for (auto* puppet : currentVtk->getPuppets()) {
|
|
newVtk->AddPuppet(*puppet);
|
|
}
|
|
// Copy camera
|
|
if (currentVtk->GetRenderer() && newVtk->GetRenderer()) {
|
|
vtkCamera* currentCam = currentVtk->GetRenderer()->GetActiveCamera();
|
|
vtkCamera* newCam = newVtk->GetRenderer()->GetActiveCamera();
|
|
if (currentCam && newCam) {
|
|
newCam->DeepCopy(currentCam);
|
|
}
|
|
}
|
|
// Sync grid visible and axis
|
|
newVtk->SetGridVisible(currentVtk->GetGridVisible());
|
|
newVtk->SetGridAxis(currentVtk->GetGridAxis());
|
|
}
|
|
}
|
|
|
|
// 2. Adjust for ROOT Canvas if that was the active view
|
|
bool isRoot = (qobject_cast<uLib::Root::QCanvas*>(m_viewport) != nullptr);
|
|
if (isRoot) {
|
|
newPane->addRootCanvas();
|
|
}
|
|
|
|
if (parentSplitter->orientation() == orientation) {
|
|
int index = parentSplitter->indexOf(this);
|
|
QList<int> sizes = parentSplitter->sizes();
|
|
int currentSize = sizes.value(index, 0);
|
|
int half = currentSize / 2;
|
|
sizes[index] = half;
|
|
sizes.insert(index + 1, currentSize - half);
|
|
|
|
parentSplitter->insertWidget(index + 1, newPane);
|
|
parentSplitter->setSizes(sizes);
|
|
} else {
|
|
int index = parentSplitter->indexOf(this);
|
|
QList<int> parentSizes = parentSplitter->sizes();
|
|
|
|
QSplitter* newSplitter = new QSplitter(orientation);
|
|
newSplitter->addWidget(this);
|
|
newSplitter->addWidget(newPane);
|
|
|
|
QList<int> subSizes;
|
|
subSizes << 500 << 500;
|
|
newSplitter->setSizes(subSizes);
|
|
|
|
parentSplitter->insertWidget(index, newSplitter);
|
|
parentSplitter->setSizes(parentSizes);
|
|
}
|
|
}
|