diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 0aa7b8047f..e57dd74fe2 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -1011,7 +1011,7 @@ void Menu::bandwidthDetails() { } void Menu::showMetavoxelEditor() { - if (_metavoxelEditorDialog.isNull()) { + if (!_metavoxelEditorDialog) { _metavoxelEditorDialog = new MetavoxelEditorDialog(); } _metavoxelEditorDialog->raise(); diff --git a/interface/src/ui/MetavoxelEditorDialog.cpp b/interface/src/ui/MetavoxelEditorDialog.cpp index d5903d6d43..1a1cac3adf 100644 --- a/interface/src/ui/MetavoxelEditorDialog.cpp +++ b/interface/src/ui/MetavoxelEditorDialog.cpp @@ -5,6 +5,16 @@ // Created by Andrzej Kapolka on 1/21/14. // Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +#include +#include +#include +#include +#include +#include +#include + +#include + #include "Application.h" #include "MetavoxelEditorDialog.h" @@ -13,6 +23,103 @@ MetavoxelEditorDialog::MetavoxelEditorDialog() : setWindowTitle("Metavoxel Editor"); setAttribute(Qt::WA_DeleteOnClose); + + QVBoxLayout* topLayout = new QVBoxLayout(); + setLayout(topLayout); + + QGroupBox* attributeGroup = new QGroupBox(); + attributeGroup->setTitle("Attributes"); + topLayout->addWidget(attributeGroup); + + QVBoxLayout* attributeLayout = new QVBoxLayout(); + attributeGroup->setLayout(attributeLayout); + + attributeLayout->addWidget(_attributes = new QListWidget()); + connect(_attributes, SIGNAL(itemSelectionChanged()), SLOT(updateValueEditor())); + + QPushButton* newAttribute = new QPushButton("New..."); + attributeLayout->addWidget(newAttribute); + connect(newAttribute, SIGNAL(clicked()), SLOT(createNewAttribute())); + + _value = new QGroupBox(); + _value->setTitle("Value"); + topLayout->addWidget(_value); + + QVBoxLayout* valueLayout = new QVBoxLayout(); + _value->setLayout(valueLayout); + + updateAttributes(); show(); } + +void MetavoxelEditorDialog::updateValueEditor() { + QString selected = getSelectedAttribute(); + if (selected.isNull()) { + _value->setVisible(false); + return; + } + _value->setVisible(true); + + if (!_value->layout()->isEmpty()) { + delete _value->layout()->takeAt(0); + } + + AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(selected); + QWidget* editor = attribute->createEditor(); + if (editor) { + _value->layout()->addWidget(editor); + } +} + +void MetavoxelEditorDialog::createNewAttribute() { + QDialog dialog(this); + dialog.setWindowTitle("New Attribute"); + + QVBoxLayout layout; + dialog.setLayout(&layout); + + QFormLayout form; + layout.addLayout(&form); + + QLineEdit name; + form.addRow("Name:", &name); + + QDialogButtonBox buttons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + dialog.connect(&buttons, SIGNAL(accepted()), SLOT(accept())); + dialog.connect(&buttons, SIGNAL(rejected()), SLOT(reject())); + + layout.addWidget(&buttons); + + if (!dialog.exec()) { + return; + } + QString nameText = name.text().trimmed(); + AttributeRegistry::getInstance()->registerAttribute(new QRgbAttribute(nameText)); + + updateAttributes(nameText); +} + +void MetavoxelEditorDialog::updateAttributes(const QString& select) { + // remember the selection in order to preserve it + QString selected = select.isNull() ? getSelectedAttribute() : select; + _attributes->clear(); + + // sort the names for consistent ordering + QList names = AttributeRegistry::getInstance()->getAttributes().keys(); + qSort(names); + + foreach (const QString& name, names) { + QListWidgetItem* item = new QListWidgetItem(name); + _attributes->addItem(item); + if (name == selected || selected.isNull()) { + item->setSelected(true); + selected = name; + } + } +} + +QString MetavoxelEditorDialog::getSelectedAttribute() const { + QList selectedItems = _attributes->selectedItems(); + return selectedItems.isEmpty() ? QString() : selectedItems.first()->text(); +} diff --git a/interface/src/ui/MetavoxelEditorDialog.h b/interface/src/ui/MetavoxelEditorDialog.h index 14ebf62109..15eaac7801 100644 --- a/interface/src/ui/MetavoxelEditorDialog.h +++ b/interface/src/ui/MetavoxelEditorDialog.h @@ -11,6 +11,9 @@ #include +class QGroupBox; +class QListWidget; + /// Allows editing metavoxels. class MetavoxelEditorDialog : public QDialog { Q_OBJECT @@ -18,6 +21,19 @@ class MetavoxelEditorDialog : public QDialog { public: MetavoxelEditorDialog(); + +private slots: + + void updateValueEditor(); + void createNewAttribute(); + +private: + + void updateAttributes(const QString& select = QString()); + QString getSelectedAttribute() const; + + QListWidget* _attributes; + QGroupBox* _value; }; #endif /* defined(__interface__MetavoxelEditorDialog__) */ diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index 9a1f220034..accf9d81cf 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -6,7 +6,10 @@ // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // +#include +#include #include +#include #include "AttributeRegistry.h" #include "MetavoxelData.h" @@ -132,6 +135,31 @@ void* QRgbAttribute::createFromScript(const QScriptValue& value, QScriptEngine* return encodeInline((QRgb)value.toUInt32()); } +QWidget* QRgbAttribute::createEditor(QWidget* parent) const { + QRgbEditor* editor = new QRgbEditor(parent); + editor->setColor(_defaultValue); + return editor; +} + +QRgbEditor::QRgbEditor(QWidget* parent) : QWidget(parent) { + setLayout(new QVBoxLayout()); + layout()->addWidget(_button = new QPushButton()); + connect(_button, SIGNAL(clicked()), SLOT(selectColor())); +} + +void QRgbEditor::setColor(int color) { + QString name = QColor::fromRgba(_color = color).name(); + _button->setStyleSheet(QString("background: %1; color: %2").arg(name, QColor::fromRgb(~color).name())); + _button->setText(name); +} + +void QRgbEditor::selectColor() { + QColor color = QColorDialog::getColor(QColor::fromRgba(_color), this, QString(), QColorDialog::ShowAlphaChannel); + if (color.isValid()) { + setColor(color.rgba()); + } +} + PolymorphicData::~PolymorphicData() { } diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 4f2f1d79b2..3501c289c6 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -16,9 +16,11 @@ #include #include #include +#include #include "Bitstream.h" +class QPushButton; class QScriptContext; class QScriptEngine; class QScriptValue; @@ -52,6 +54,9 @@ public: /// Retrieves an attribute by name. AttributePointer getAttribute(const QString& name) const { return _attributes.value(name); } + /// Returns a reference to the attribute hash. + const QHash& getAttributes() const { return _attributes; } + /// Returns a reference to the standard PolymorphicDataPointer "guide" attribute. const AttributePointer& getGuideAttribute() const { return _guideAttribute; } @@ -153,6 +158,10 @@ public: virtual void* getDefaultValue() const = 0; virtual void* createFromScript(const QScriptValue& value, QScriptEngine* engine) const { return create(); } + + /// Creates a widget to use to edit values of this attribute, or returns NULL if the attribute isn't editable. + /// The widget should have a single "user" property that will be used to get/set the value. + virtual QWidget* createEditor(QWidget* parent = NULL) const { return NULL; } }; /// A simple attribute class that stores its values inline. @@ -222,6 +231,31 @@ public: virtual bool merge(void*& parent, void* children[]) const; virtual void* createFromScript(const QScriptValue& value, QScriptEngine* engine) const; + + virtual QWidget* createEditor(QWidget* parent = NULL) const; +}; + +/// Editor for RGBA values. +class QRgbEditor : public QWidget { + Q_OBJECT + Q_PROPERTY(int color MEMBER _color WRITE setColor USER true) + +public: + + QRgbEditor(QWidget* parent); + +public slots: + + void setColor(int color); + +private slots: + + void selectColor(); + +private: + + QPushButton* _button; + QRgb _color; }; /// An attribute class that stores pointers to its values.