diff --git a/src/designer-plugins/toolbox.cpp b/src/designer-plugins/toolbox.cpp index c75ef44..eb3b768 100644 --- a/src/designer-plugins/toolbox.cpp +++ b/src/designer-plugins/toolbox.cpp @@ -17,11 +17,10 @@ using namespace Qtk; ToolBox::ToolBox(QWidget * parent) : - QDockWidget(parent), objectDetails_(this), - transformPanel_(this, "Transform:"), scalePanel_(this, "Scale:"), - vertex_(this, "Vertex Shader:"), fragment_(this, "Fragment Shader:"), - properiesForm_(new QFormLayout), shaderForm_(new QFormLayout), - ui(new Ui::ToolBox) + QDockWidget(parent), objectDetails_(this), transformPanel_(this), + scalePanel_(this), vertex_(this, "Vertex Shader:"), + fragment_(this, "Fragment Shader:"), properiesForm_(new QFormLayout), + shaderForm_(new QFormLayout), ui(new Ui::ToolBox) { ui->setupUi(this); setMinimumWidth(350); @@ -51,17 +50,60 @@ void ToolBox::updateFocus(const QString & name) } } -ToolBox::SpinBox3D::SpinBox3D(QLayout * layout, const char * l) : - label(new QLabel(tr(l))), x(new QDoubleSpinBox), y(new QDoubleSpinBox), - z(new QDoubleSpinBox), fields({x, y, z}) +ToolBox::SpinBox3D::SpinBox3D(QWidget * parent, const char * l) : + QWidget(parent), layout(new QHBoxLayout(this)), label(new QLabel(tr(l))) { // The layout owns the widget and will clean it up on destruction. layout->addWidget(label); for (const auto & f : fields) { - layout->addWidget(f); - f->setMinimum(std::numeric_limits::lowest()); - f->setSingleStep(0.1); - f->setFixedWidth(75); + layout->addWidget(f->spinBox); + f->spinBox->setMinimum(std::numeric_limits::lowest()); + f->spinBox->setSingleStep(0.1); + f->spinBox->setFixedWidth(75); + } +} + +void ToolBox::SpinBox::disconnect() const +{ + Object::disconnect(connection); +} + +void ToolBox::TransformPanel::setObject(const Qtk::Object * object) +{ + // Reconnect translation panel controls to the new object. + const std::vector binds = {&Object::setTranslationX, + &Object::setTranslationY, + &Object::setTranslationZ}; + for (size_t i = 0; i < spinBox3D.fields.size(); i++) { + auto * f = spinBox3D.fields[i]; + // Disconnect before changing spin box value. + f->disconnect(); + + // Set the values in the spin box to the object's current X,Y,Z + f->spinBox->setValue(object->getTransform().getTranslation()[i]); + + // Reconnect to bind spin box value to the new object's position. + f->connection = + connect(f->spinBox, &QDoubleSpinBox::valueChanged, object, binds[i]); + } +} + +void ToolBox::ScalePanel::setObject(const Qtk::Object * object) +{ + // Reconnect scale panel controls to the new object. + const std::vector binds = { + &Object::setScaleX, &Object::setScaleY, &Object::setScaleZ}; + for (size_t i = 0; i < spinBox3D.fields.size(); i++) { + auto * f = spinBox3D.fields[i]; + // Disconnect before changing spin box value. + f->disconnect(); + + // Set the values in the spin box to the object's current X,Y,Z + f->spinBox->setValue(object->getTransform().getScale()[i]); + + // Reconnect to bind spin box value to the new object's scale. + f->connection = + connect(f->spinBox, &QDoubleSpinBox::valueChanged, object, binds[i]); } } @@ -72,44 +114,11 @@ ToolBox::~ToolBox() void ToolBox::refreshProperties(const Object * object) { - objectDetails_.setDetails(object); - + // Refresh to show the new object's details. + objectDetails_.setObject(object); // Reconnect transform panel controls to the new object. - connect(transformPanel_.spinBox.x, - &QDoubleSpinBox::valueChanged, - object, - &Object::setTranslationX); - connect(transformPanel_.spinBox.y, - &QDoubleSpinBox::valueChanged, - object, - &Object::setTranslationY); - connect(transformPanel_.spinBox.z, - &QDoubleSpinBox::valueChanged, - object, - &Object::setTranslationZ); - // Set the values in the spin box to the object's current X,Y,Z position. - auto transform = object->getTransform(); - for (size_t i = 0; i < 3; i++) { - transformPanel_.spinBox.fields[i]->setValue(transform.getTranslation()[i]); - } - - // Reconnect scale panel controls to the new object. - connect(scalePanel_.spinBox.x, - &QDoubleSpinBox::valueChanged, - object, - &Object::setScaleX); - connect(scalePanel_.spinBox.y, - &QDoubleSpinBox::valueChanged, - object, - &Object::setScaleY); - connect(scalePanel_.spinBox.z, - &QDoubleSpinBox::valueChanged, - object, - &Object::setScaleZ); - // Set the values in the spin box to the object's current X,Y,Z scale. - for (size_t i = 0; i < 3; i++) { - scalePanel_.spinBox.fields[i]->setValue(transform.getScale()[i]); - } + transformPanel_.setObject(object); + scalePanel_.setObject(object); } void ToolBox::refreshShaders(const Object * object) diff --git a/src/designer-plugins/toolbox.h b/src/designer-plugins/toolbox.h index 4a172ba..2ab4fbd 100644 --- a/src/designer-plugins/toolbox.h +++ b/src/designer-plugins/toolbox.h @@ -84,7 +84,8 @@ namespace Qtk { } - void setDetails(const Qtk::Object * object) + /// Refresh to display the new object's details + void setObject(const Qtk::Object * object) { name.setItem(tr("Name:"), object->getName()); objectType.setItem( @@ -96,32 +97,67 @@ namespace Qtk }; ObjectDetails objectDetails_; - /// Spinbox with 3 fields and label. - struct SpinBox3D { - /// We pass a layout to ensure Qt will clean up resources. - explicit SpinBox3D(QLayout * layout, const char * l = "SpinBox3D:"); + /// Structure to associate a QSpinBox with a connection. + struct SpinBox { + /** + * Default constructor passes no parent to the QSpinBox. + * It must be added to a layout for Qt to clean up the resources. + */ + SpinBox() : spinBox(new QDoubleSpinBox) {} - QLabel * label; - QDoubleSpinBox * x; - QDoubleSpinBox * y; - QDoubleSpinBox * z; - std::array fields; + /// Disconnect the associated connection. + void disconnect() const; + + QDoubleSpinBox * spinBox; + QMetaObject::Connection connection; }; - /// Transform controls and layout. - class SpinBoxHorizontal3D : QWidget + /// Spinbox with 3 fields and a single label. + class SpinBox3D final : QWidget { public: - explicit SpinBoxHorizontal3D( - QWidget * parent, const char * l = "SpinBoxHorizontal3D:") : - QWidget(parent), layout(new QHBoxLayout(this)), spinBox(layout, l) + /// We pass a parent to ensure Qt will clean up resources. + /// Assigning a QWidget to a QLayout also ensures Qt will clean up. + explicit SpinBox3D(QWidget * parent, const char * l = "SpinBox3D:"); + + /// The main layout for the SpinBox3D widget. + QHBoxLayout * layout {this}; + + /// Label for the SpinBox3D. + QLabel * label; + + /// SpinBox and a connection for each field. + SpinBox x, y, z; + /// Array for iterating over fields. + std::array fields {&x, &y, &z}; + }; + + /// Initialize the transform panel and configure QObject connections. + struct TransformPanel { + explicit TransformPanel(QWidget * parent) : + spinBox3D(parent, "Transform:") { } - QHBoxLayout * layout; - SpinBox3D spinBox; - }; - SpinBoxHorizontal3D transformPanel_, scalePanel_; + /// Reconnect QObject connections and spin box values in UI. + void setObject(const Qtk::Object * object); + + SpinBox3D spinBox3D; + }; + TransformPanel transformPanel_; + + /// Initialize the scale panel and configure QObject connections. + struct ScalePanel { + explicit ScalePanel(QWidget * parent) : spinBox3D(parent, "Scale:") {} + + /// Reconnect QObject connections and spin box values in UI. + void setObject(const Qtk::Object * object); + + SpinBox3D spinBox3D; + }; + ScalePanel scalePanel_; + + /// Displays shader name, path, and read-only text view. class ShaderView final : QWidget { public: @@ -130,17 +166,23 @@ namespace Qtk layout(new QVBoxLayout(this)), path(parent, l), editor(new QTextEdit(parent)) { + // Create a child horizontal layout for shader name and file path. auto * pathLayout = new QHBoxLayout; pathLayout->addWidget(path.label); pathLayout->addWidget(path.value); layout->addLayout(pathLayout); - layout->addWidget(editor); + + // Add the read-only text editor widget to the main layout. editor->setReadOnly(true); + layout->addWidget(editor); } + /// The main layout for the ShaderView widget. QVBoxLayout * layout; + /// Shader name and path on disk. ObjectDetails::Item path; + /// Read-only (for now) display of the shader source code. QTextEdit * editor; }; diff --git a/src/qtk/object.h b/src/qtk/object.h index 73211ae..14cf28e 100644 --- a/src/qtk/object.h +++ b/src/qtk/object.h @@ -244,6 +244,21 @@ namespace Qtk mProgram.release(); } + /************************************************************************* + * Public Static Methods + ************************************************************************/ + + /** + * Helper to disconnect a QObject connection, only if it's valid. + * If the connection is valid and we fail to disconnect log a message. + */ + static void disconnect(const QMetaObject::Connection & con) + { + if (con && !QObject::disconnect(con)) { + qDebug() << "[Qtk] Failed to disconnect valid connection: " << con; + } + } + private: /************************************************************************* * Private Members