#include "vtkQViewport.h" #include #include #include #include #include #include #include #include #include namespace uLib { namespace Vtk { QViewport::QViewport(QWidget* parent) : QWidget(parent) , Viewport() , m_VtkWidget(nullptr) , m_GridButton(nullptr) , m_ProjButton(nullptr) , m_renderTimer(nullptr) , m_renderPending(false) { // Build the layout – zero margins so VTK fills the entire widget auto* layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); m_VtkWidget = new QVTKOpenGLNativeWidget(this); layout->addWidget(m_VtkWidget); // Initialize render timer m_renderTimer = new QTimer(this); m_renderTimer->setSingleShot(true); connect(m_renderTimer, &QTimer::timeout, this, &QViewport::doRender); // Grid Toggle Button m_GridButton = new QPushButton(m_VtkWidget); m_GridButton->setText("#"); m_GridButton->setFixedSize(40, 40); m_GridButton->setToolTip("Toggle Grid"); m_GridButton->setStyleSheet( "QPushButton {" " border-radius: 20px;" // Perfectly circular " background-color: rgba(40, 40, 40, 180);" " color: white;" " font-size: 22px;" " border: 1.5px solid rgba(255, 255, 255, 60);" "}" "QPushButton:hover {" " background-color: rgba(70, 70, 70, 200);" " border: 1.5px solid rgba(255, 255, 255, 100);" "}" "QPushButton:checked {" " background-color: rgba(0, 120, 215, 200);" // Nice "active" blue " color: white;" " border: 1.5px solid rgba(255, 255, 255, 120);" "}" "QPushButton:pressed {" " background-color: rgba(0, 90, 160, 220);" "}" ); m_GridButton->setCheckable(true); m_GridButton->setChecked(true); // Grid is on by default connect(m_GridButton, &QPushButton::clicked, this, &QViewport::onGridButtonClicked); // Projection Toggle Button (below grid button) m_ProjButton = new QPushButton(m_VtkWidget); m_ProjButton->setText("P"); m_ProjButton->setFixedSize(40, 40); m_ProjButton->setToolTip("Toggle Perspective / Orthographic"); m_ProjButton->setStyleSheet( "QPushButton {" " border-radius: 20px;" " background-color: rgba(40, 40, 40, 180);" " color: white;" " font-size: 22px;" " border: 1.5px solid rgba(255, 255, 255, 60);" "}" "QPushButton:hover {" " background-color: rgba(70, 70, 70, 200);" " border: 1.5px solid rgba(255, 255, 255, 100);" "}" "QPushButton:checked {" " background-color: rgba(0, 120, 215, 200);" " color: white;" " border: 1.5px solid rgba(255, 255, 255, 120);" "}" "QPushButton:pressed {" " background-color: rgba(0, 90, 160, 220);" "}" ); m_ProjButton->setCheckable(true); m_ProjButton->setChecked(false); // Perspective by default connect(m_ProjButton, &QPushButton::clicked, this, &QViewport::onProjButtonClicked); // After the Qt widget exists but before the first paint, // attach the renderer and configure the pipeline. SetupPipeline(); } QViewport::~QViewport() { if (m_VtkWidget && m_VtkWidget->renderWindow()) { m_VtkWidget->renderWindow()->RemoveRenderer(this->GetRenderer()); } } void QViewport::SetupPipeline() { // Add our renderer to the QVTKOpenGLNativeWidget's render window ::vtkRenderWindow* rw = m_VtkWidget->renderWindow(); rw->AddRenderer(this->GetRenderer()); // Common setup Viewport::SetupPipeline(rw->GetInteractor()); } // ── Public API ──────────────────────────────────────────────────────────────── void QViewport::Render() { if (!m_throttledRendering) { doRender(); return; } if (m_renderPending) return; m_renderPending = true; m_renderTimer->start(16); } void QViewport::doRender() { m_renderPending = false; if (m_VtkWidget && m_VtkWidget->renderWindow()) m_VtkWidget->renderWindow()->Render(); } vtkRenderWindow* QViewport::GetRenderWindow() { return m_VtkWidget->renderWindow(); } vtkRenderWindowInteractor* QViewport::GetInteractor() { return m_VtkWidget->renderWindow()->GetInteractor(); } void QViewport::onGridButtonClicked() { SetGridVisible(m_GridButton->isChecked()); } void QViewport::onProjButtonClicked() { SetParallelProjection(m_ProjButton->isChecked()); } void QViewport::OnSelectionChanged(Prop3D* p) { emit prop3dSelected(p); } void QViewport::resizeEvent(QResizeEvent* event) { QWidget::resizeEvent(event); if (m_GridButton) { // Position under the gizmo (top-right corner). // Standard CameraOrientationWidget is usually 150-180px. int x = width() - m_GridButton->width() - 10; int y = 160; m_GridButton->move(x, y); } if (m_ProjButton) { int x = width() - m_ProjButton->width() - 10; int y = 210; m_ProjButton->move(x, y); } } } // namespace Vtk } // namespace uLib