feat: add Preferences dialog for managing theme, rendering, and unit settings
This commit is contained in:
@@ -17,6 +17,8 @@ add_executable(gcompose
|
|||||||
src/PropertyWidgets.cpp
|
src/PropertyWidgets.cpp
|
||||||
src/PropertiesPanel.h
|
src/PropertiesPanel.h
|
||||||
src/PropertiesPanel.cpp
|
src/PropertiesPanel.cpp
|
||||||
|
src/PreferencesDialog.h
|
||||||
|
src/PreferencesDialog.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set_target_properties(gcompose PROPERTIES
|
set_target_properties(gcompose PROPERTIES
|
||||||
|
|||||||
@@ -18,6 +18,8 @@
|
|||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include "StyleManager.h"
|
#include "StyleManager.h"
|
||||||
#include "Math/VoxImage.h"
|
#include "Math/VoxImage.h"
|
||||||
|
#include "PreferencesDialog.h"
|
||||||
|
#include "Settings.h"
|
||||||
|
|
||||||
MainPanel::MainPanel(QWidget* parent) : QWidget(parent), m_context(nullptr), m_mainVtkContext(nullptr) {
|
MainPanel::MainPanel(QWidget* parent) : QWidget(parent), m_context(nullptr), m_mainVtkContext(nullptr) {
|
||||||
this->setObjectName("MainPanel");
|
this->setObjectName("MainPanel");
|
||||||
@@ -45,17 +47,12 @@ MainPanel::MainPanel(QWidget* parent) : QWidget(parent), m_context(nullptr), m_m
|
|||||||
fileMenu->addAction("Open", this, &MainPanel::onOpen);
|
fileMenu->addAction("Open", this, &MainPanel::onOpen);
|
||||||
fileMenu->addAction("Save", this, &MainPanel::onSave);
|
fileMenu->addAction("Save", this, &MainPanel::onSave);
|
||||||
fileMenu->addAction("Save As", this, &MainPanel::onSaveAs);
|
fileMenu->addAction("Save As", this, &MainPanel::onSaveAs);
|
||||||
|
fileMenu->addSeparator();
|
||||||
|
fileMenu->addAction("Preferences", this, &MainPanel::onPreferences);
|
||||||
|
fileMenu->addSeparator();
|
||||||
fileMenu->addAction("Exit", this, &MainPanel::onExit);
|
fileMenu->addAction("Exit", this, &MainPanel::onExit);
|
||||||
btnFile->setMenu(fileMenu);
|
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);
|
|
||||||
|
|
||||||
// New Menu Button
|
// New Menu Button
|
||||||
auto* btnNew = new QPushButton("Add", menuPanel);
|
auto* btnNew = new QPushButton("Add", menuPanel);
|
||||||
btnNew->setObjectName("MenuButton");
|
btnNew->setObjectName("MenuButton");
|
||||||
@@ -73,7 +70,6 @@ MainPanel::MainPanel(QWidget* parent) : QWidget(parent), m_context(nullptr), m_m
|
|||||||
menuLayout->addWidget(logo);
|
menuLayout->addWidget(logo);
|
||||||
menuLayout->addWidget(btnFile);
|
menuLayout->addWidget(btnFile);
|
||||||
menuLayout->addWidget(btnNew);
|
menuLayout->addWidget(btnNew);
|
||||||
menuLayout->addWidget(btnTheme);
|
|
||||||
menuLayout->addStretch();
|
menuLayout->addStretch();
|
||||||
|
|
||||||
mainLayout->addWidget(menuPanel);
|
mainLayout->addWidget(menuPanel);
|
||||||
@@ -236,12 +232,21 @@ void MainPanel::onExit() {
|
|||||||
qApp->quit();
|
qApp->quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainPanel::onDarkTheme() {
|
void MainPanel::onPreferences() {
|
||||||
StyleManager::applyStyle(qApp, "dark");
|
uLib::Qt::PreferencesDialog dlg(this);
|
||||||
}
|
if (dlg.exec() == QDialog::Accepted) {
|
||||||
|
// Apply theme
|
||||||
|
auto theme = uLib::Qt::Settings::Instance().GetTheme();
|
||||||
|
StyleManager::applyStyle(qApp, theme == uLib::Qt::Settings::Dark ? "dark" : "bright");
|
||||||
|
|
||||||
void MainPanel::onBrightTheme() {
|
// Apply rendering preference to all viewports
|
||||||
StyleManager::applyStyle(qApp, "bright");
|
bool throttled = uLib::Qt::Settings::Instance().GetThrottledRendering();
|
||||||
|
auto viewports = this->findChildren<uLib::Vtk::QViewport*>();
|
||||||
|
for (auto* vp : viewports) {
|
||||||
|
vp->SetThrottledRendering(throttled);
|
||||||
|
vp->Render();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MainPanel::~MainPanel() {}
|
MainPanel::~MainPanel() {}
|
||||||
|
|||||||
@@ -30,8 +30,7 @@ private slots:
|
|||||||
void onSaveAs();
|
void onSaveAs();
|
||||||
void onExit();
|
void onExit();
|
||||||
|
|
||||||
void onDarkTheme();
|
void onPreferences();
|
||||||
void onBrightTheme();
|
|
||||||
|
|
||||||
void onCreateObject(const std::string& className);
|
void onCreateObject(const std::string& className);
|
||||||
|
|
||||||
|
|||||||
99
app/gcompose/src/PreferencesDialog.cpp
Normal file
99
app/gcompose/src/PreferencesDialog.cpp
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
#include "PreferencesDialog.h"
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QFormLayout>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QGroupBox>
|
||||||
|
|
||||||
|
namespace uLib {
|
||||||
|
namespace Qt {
|
||||||
|
|
||||||
|
PreferencesDialog::PreferencesDialog(QWidget* parent) : QDialog(parent) {
|
||||||
|
setWindowTitle("Preferences");
|
||||||
|
setMinimumWidth(400);
|
||||||
|
|
||||||
|
auto* mainLayout = new QVBoxLayout(this);
|
||||||
|
mainLayout->setSpacing(20);
|
||||||
|
mainLayout->setContentsMargins(20, 20, 20, 20);
|
||||||
|
|
||||||
|
// ── General / Rendering Settings ────────────────────────────────────────
|
||||||
|
auto* renderingGroup = new QGroupBox("Appearance & Performance", this);
|
||||||
|
auto* renderingLayout = new QVBoxLayout(renderingGroup);
|
||||||
|
|
||||||
|
auto* themeLayout = new QHBoxLayout();
|
||||||
|
themeLayout->addWidget(new QLabel("Color Theme:"));
|
||||||
|
m_themeCombo = new QComboBox(renderingGroup);
|
||||||
|
m_themeCombo->addItem("Dark");
|
||||||
|
m_themeCombo->addItem("Bright");
|
||||||
|
m_themeCombo->setCurrentIndex(Settings::Instance().GetTheme() == Settings::Dark ? 0 : 1);
|
||||||
|
themeLayout->addWidget(m_themeCombo);
|
||||||
|
themeLayout->addStretch();
|
||||||
|
|
||||||
|
renderingLayout->addLayout(themeLayout);
|
||||||
|
renderingLayout->addSpacing(10);
|
||||||
|
|
||||||
|
m_throttledRendering = new QCheckBox("Enable throttled rendering (recommended for performance)", renderingGroup);
|
||||||
|
m_throttledRendering->setChecked(Settings::Instance().GetThrottledRendering());
|
||||||
|
m_throttledRendering->setToolTip("Limits framerate to ~60fps to reduce CPU/GPU usage.");
|
||||||
|
|
||||||
|
renderingLayout->addWidget(m_throttledRendering);
|
||||||
|
mainLayout->addWidget(renderingGroup);
|
||||||
|
|
||||||
|
// ── Units Settings ──────────────────────────────────────────────────────
|
||||||
|
auto* unitsGroup = new QGroupBox("Preferred Units", this);
|
||||||
|
auto* unitsLayout = new QFormLayout(unitsGroup);
|
||||||
|
unitsLayout->setLabelAlignment(::Qt::AlignRight);
|
||||||
|
unitsLayout->setSpacing(10);
|
||||||
|
|
||||||
|
auto addUnitRow = [&](const QString& label, Settings::Dimension dim, const QStringList& units) {
|
||||||
|
auto* combo = new QComboBox(unitsGroup);
|
||||||
|
combo->addItems(units);
|
||||||
|
std::string current = Settings::Instance().GetPreferredUnit(dim);
|
||||||
|
int idx = combo->findText(QString::fromStdString(current));
|
||||||
|
if (idx >= 0) combo->setCurrentIndex(idx);
|
||||||
|
|
||||||
|
unitsLayout->addRow(label + ":", combo);
|
||||||
|
m_unitCombos[dim] = combo;
|
||||||
|
};
|
||||||
|
|
||||||
|
addUnitRow("Length", Settings::Length, {"m", "cm", "mm", "um", "nm"});
|
||||||
|
addUnitRow("Angle", Settings::Angle, {"deg", "rad"});
|
||||||
|
addUnitRow("Energy", Settings::Energy, {"MeV", "GeV", "eV", "keV", "TeV"});
|
||||||
|
addUnitRow("Time", Settings::Time, {"ns", "s", "ms", "us"});
|
||||||
|
|
||||||
|
mainLayout->addWidget(unitsGroup);
|
||||||
|
|
||||||
|
mainLayout->addStretch();
|
||||||
|
|
||||||
|
// ── Buttons ─────────────────────────────────────────────────────────────
|
||||||
|
auto* buttonLayout = new QHBoxLayout();
|
||||||
|
buttonLayout->addStretch();
|
||||||
|
|
||||||
|
auto* btnCancel = new QPushButton("Cancel", this);
|
||||||
|
connect(btnCancel, &QPushButton::clicked, this, &QDialog::reject);
|
||||||
|
|
||||||
|
auto* btnOk = new QPushButton("Apply", this);
|
||||||
|
btnOk->setDefault(true);
|
||||||
|
btnOk->setObjectName("DisplayToggleBtn"); // Reusing high-contrast style
|
||||||
|
btnOk->setMinimumWidth(100);
|
||||||
|
connect(btnOk, &QPushButton::clicked, this, &PreferencesDialog::onAccept);
|
||||||
|
|
||||||
|
buttonLayout->addWidget(btnCancel);
|
||||||
|
buttonLayout->addWidget(btnOk);
|
||||||
|
mainLayout->addLayout(buttonLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PreferencesDialog::onAccept() {
|
||||||
|
Settings::Instance().SetThrottledRendering(m_throttledRendering->isChecked());
|
||||||
|
Settings::Instance().SetTheme(m_themeCombo->currentIndex() == 0 ? Settings::Dark : Settings::Bright);
|
||||||
|
|
||||||
|
for (auto const& pair : m_unitCombos) {
|
||||||
|
Settings::Instance().SetPreferredUnit(pair.first, pair.second->currentText().toStdString());
|
||||||
|
}
|
||||||
|
|
||||||
|
accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Qt
|
||||||
|
} // namespace uLib
|
||||||
31
app/gcompose/src/PreferencesDialog.h
Normal file
31
app/gcompose/src/PreferencesDialog.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#ifndef GCOMPOSE_PREFERENCESDIALOG_H
|
||||||
|
#define GCOMPOSE_PREFERENCESDIALOG_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QCheckBox>
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
#include "Settings.h"
|
||||||
|
|
||||||
|
namespace uLib {
|
||||||
|
namespace Qt {
|
||||||
|
|
||||||
|
class PreferencesDialog : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit PreferencesDialog(QWidget* parent = nullptr);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onAccept();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QCheckBox* m_throttledRendering;
|
||||||
|
QComboBox* m_themeCombo;
|
||||||
|
std::map<Settings::Dimension, QComboBox*> m_unitCombos;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Qt
|
||||||
|
} // namespace uLib
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -23,6 +23,11 @@ public:
|
|||||||
Dimensionless
|
Dimensionless
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum Theme {
|
||||||
|
Dark,
|
||||||
|
Bright
|
||||||
|
};
|
||||||
|
|
||||||
void SetPreferredUnit(Dimension dim, const std::string& unit) {
|
void SetPreferredUnit(Dimension dim, const std::string& unit) {
|
||||||
m_PreferredUnits[dim] = unit;
|
m_PreferredUnits[dim] = unit;
|
||||||
}
|
}
|
||||||
@@ -64,9 +69,17 @@ public:
|
|||||||
return Dimensionless;
|
return Dimensionless;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GetThrottledRendering() const { return m_ThrottledRendering; }
|
||||||
|
void SetThrottledRendering(bool enabled) { m_ThrottledRendering = enabled; }
|
||||||
|
|
||||||
|
Theme GetTheme() const { return m_Theme; }
|
||||||
|
void SetTheme(Theme theme) { m_Theme = theme; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Settings() {}
|
Settings() : m_ThrottledRendering(true), m_Theme(Dark) {}
|
||||||
std::map<Dimension, std::string> m_PreferredUnits;
|
std::map<Dimension, std::string> m_PreferredUnits;
|
||||||
|
bool m_ThrottledRendering;
|
||||||
|
Theme m_Theme;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Qt
|
} // namespace Qt
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ QViewport::QViewport(QWidget* parent)
|
|||||||
, m_VtkWidget(nullptr)
|
, m_VtkWidget(nullptr)
|
||||||
, m_GridButton(nullptr)
|
, m_GridButton(nullptr)
|
||||||
, m_ProjButton(nullptr)
|
, m_ProjButton(nullptr)
|
||||||
|
, m_renderTimer(nullptr)
|
||||||
|
, m_renderPending(false)
|
||||||
{
|
{
|
||||||
// Build the layout – zero margins so VTK fills the entire widget
|
// Build the layout – zero margins so VTK fills the entire widget
|
||||||
auto* layout = new QVBoxLayout(this);
|
auto* layout = new QVBoxLayout(this);
|
||||||
@@ -29,8 +31,14 @@ QViewport::QViewport(QWidget* parent)
|
|||||||
m_VtkWidget = new QVTKOpenGLNativeWidget(this);
|
m_VtkWidget = new QVTKOpenGLNativeWidget(this);
|
||||||
layout->addWidget(m_VtkWidget);
|
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
|
// Grid Toggle Button
|
||||||
m_GridButton = new QPushButton(m_VtkWidget);
|
m_GridButton = new QPushButton(m_VtkWidget);
|
||||||
|
|
||||||
m_GridButton->setText("#");
|
m_GridButton->setText("#");
|
||||||
m_GridButton->setFixedSize(40, 40);
|
m_GridButton->setFixedSize(40, 40);
|
||||||
m_GridButton->setToolTip("Toggle Grid");
|
m_GridButton->setToolTip("Toggle Grid");
|
||||||
@@ -112,10 +120,22 @@ void QViewport::SetupPipeline()
|
|||||||
|
|
||||||
void QViewport::Render()
|
void QViewport::Render()
|
||||||
{
|
{
|
||||||
if (m_VtkWidget && m_VtkWidget->renderWindow())
|
if (!m_throttledRendering) {
|
||||||
m_VtkWidget->renderWindow()->Render();
|
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()
|
vtkRenderWindow* QViewport::GetRenderWindow()
|
||||||
{
|
{
|
||||||
return m_VtkWidget->renderWindow();
|
return m_VtkWidget->renderWindow();
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <QVTKOpenGLNativeWidget.h>
|
#include <QVTKOpenGLNativeWidget.h>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
#include <vtkCornerAnnotation.h>
|
#include <vtkCornerAnnotation.h>
|
||||||
#include <vtkOrientationMarkerWidget.h>
|
#include <vtkOrientationMarkerWidget.h>
|
||||||
@@ -39,6 +40,9 @@ public:
|
|||||||
// Render scene
|
// Render scene
|
||||||
virtual void Render() override;
|
virtual void Render() override;
|
||||||
|
|
||||||
|
void SetThrottledRendering(bool enabled) { m_throttledRendering = enabled; }
|
||||||
|
bool GetThrottledRendering() const { return m_throttledRendering; }
|
||||||
|
|
||||||
// Direct access to VTK internals
|
// Direct access to VTK internals
|
||||||
virtual vtkRenderWindow* GetRenderWindow() override;
|
virtual vtkRenderWindow* GetRenderWindow() override;
|
||||||
virtual vtkRenderWindowInteractor* GetInteractor() override;
|
virtual vtkRenderWindowInteractor* GetInteractor() override;
|
||||||
@@ -46,20 +50,25 @@ public:
|
|||||||
|
|
||||||
virtual void OnSelectionChanged(Prop3D* p) override;
|
virtual void OnSelectionChanged(Prop3D* p) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void resizeEvent(QResizeEvent* event) override;
|
virtual void resizeEvent(QResizeEvent* event) override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onGridButtonClicked();
|
void onGridButtonClicked();
|
||||||
void onProjButtonClicked();
|
void onProjButtonClicked();
|
||||||
|
void doRender();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SetupPipeline();
|
void SetupPipeline();
|
||||||
|
|
||||||
|
QVTKOpenGLNativeWidget* m_VtkWidget;
|
||||||
|
QPushButton* m_GridButton;
|
||||||
|
QPushButton* m_ProjButton;
|
||||||
|
QTimer* m_renderTimer;
|
||||||
|
bool m_renderPending = false;
|
||||||
|
bool m_throttledRendering = true;
|
||||||
|
};
|
||||||
|
|
||||||
QVTKOpenGLNativeWidget* m_VtkWidget;
|
|
||||||
QPushButton* m_GridButton;
|
|
||||||
QPushButton* m_ProjButton;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Vtk
|
} // namespace Vtk
|
||||||
} // namespace uLib
|
} // namespace uLib
|
||||||
|
|||||||
Reference in New Issue
Block a user