Sun, 13 Mar 2022 14:51:39 +0200
fix saving
add color editing to object editor
--- a/CMakeLists.txt Wed Mar 09 14:22:22 2022 +0200 +++ b/CMakeLists.txt Sun Mar 13 14:51:39 2022 +0200 @@ -71,6 +71,7 @@ src/ui/objecteditor.cpp src/ui/polygonobjecteditor.cpp src/widgets/colorbutton.cpp + src/widgets/colorindexinput.cpp src/widgets/colorselectdialog.cpp src/widgets/doublespinbox.cpp src/widgets/matrixeditor.cpp @@ -130,6 +131,7 @@ src/ui/objecteditor.h src/ui/polygonobjecteditor.h src/widgets/colorbutton.h + src/widgets/colorindexinput.h src/widgets/colorselectdialog.h src/widgets/doublespinbox.h src/widgets/matrixeditor.h @@ -146,6 +148,7 @@ src/settingseditor/settingseditor.ui src/ui/multiplyfactordialog.ui src/widgets/colorselectdialog.ui + src/widgets/colorindexinput.ui src/widgets/matrixeditor.ui src/widgets/vec3editor.ui )
--- a/src/colors.cpp Wed Mar 09 14:22:22 2022 +0200 +++ b/src/colors.cpp Sun Mar 13 14:51:39 2022 +0200 @@ -178,6 +178,19 @@ } } +QColor ldraw::colorEdge(ldraw::Color color, const ldraw::ColorTable& colorTable) +{ + if (isDirectColor(color)) + { + QColor const faceColor = directColorFace(color); + return (luma(faceColor) < 0.4) ? Qt::white : Qt::black; + } + else + { + return colorTable[color].faceColor; + } +} + /** * @brief Writes a color index into a @c QDataStream * @param stream @@ -199,3 +212,15 @@ { return stream >> color.index; } + +QString ldraw::colorDisplayName(ldraw::Color color, const ldraw::ColorTable &colorTable) +{ + if (isDirectColor(color)) + { + return directColorFace(color).name(); + } + else + { + return colorTable[color].displayName; + } +}
--- a/src/colors.h Wed Mar 09 14:22:22 2022 +0200 +++ b/src/colors.h Sun Mar 13 14:51:39 2022 +0200 @@ -29,6 +29,8 @@ bool isDirectColor(Color color); QColor directColorFace(Color color); QColor colorFace(Color color, const ColorTable& colorTable); + QColor colorEdge(Color color, const ColorTable& colorTable); + QString colorDisplayName(Color color, const ColorTable& colorTable); } /**
--- a/src/document.cpp Wed Mar 09 14:22:22 2022 +0200 +++ b/src/document.cpp Sun Mar 13 14:51:39 2022 +0200 @@ -33,9 +33,9 @@ const ldraw::ColorTable& colorTable, QWidget* parent) : QWidget{parent}, + colorTable{colorTable}, model{model}, documents{documents}, - colorTable{colorTable}, vertexMap{model}, renderer{new Canvas{model, documents, colorTable, this}}, ui{*new Ui::Document}, @@ -234,7 +234,7 @@ this->renderer->adjustGridToView(); } -const Model &Document::getModel() +const Model &Document::getModel() const { return *this->model; }
--- a/src/document.h Wed Mar 09 14:22:22 2022 +0200 +++ b/src/document.h Sun Mar 13 14:51:39 2022 +0200 @@ -48,8 +48,9 @@ void applyToVertices(VertexMap::ApplyFunction fn) const; void handleKeyPress(QKeyEvent* event); void adjustGridToView(); - const Model& getModel(); + const Model& getModel() const; const QSet<ldraw::id_t> selectedObjects() const; + const ldraw::ColorTable& colorTable; Q_SIGNALS: void newStatusText(const QString& newStatusText); void splitterChanged(); @@ -62,7 +63,6 @@ void selectTool(class BaseTool* tool); Model* model; DocumentManager* const documents; - const ldraw::ColorTable& colorTable; VertexMap vertexMap; Canvas* renderer; Ui::Document& ui;
--- a/src/mainwindow.cpp Wed Mar 09 14:22:22 2022 +0200 +++ b/src/mainwindow.cpp Sun Mar 13 14:51:39 2022 +0200 @@ -224,6 +224,11 @@ return qobject_cast<Document*>(this->ui->tabs->currentWidget()); } +const Document* MainWindow::currentDocument() const +{ + return qobject_cast<const Document*>(this->ui->tabs->currentWidget()); +} + void MainWindow::handleDocumentSplitterChange() { Document* currentDocument = this->currentDocument(); @@ -285,24 +290,27 @@ { if (this->currentDocument() != nullptr) { - const ModelId modelId = {0}; - const QString* path = this->documents.modelPath(modelId); - if (path == nullptr or path->isEmpty()) + const std::optional<ModelId> modelId = this->findCurrentModelId(); + if (modelId.has_value()) { - this->actionSaveAs(); - } - else - { - QString error; - QTextStream errorStream{&error}; - const bool succeeded = this->documents.saveModel(modelId, errorStream); - if (not succeeded) + const QString* path = this->documents.modelPath(*modelId); + if (path == nullptr or path->isEmpty()) { - QMessageBox::critical(this, tr("Save error"), error); + this->actionSaveAs(); } else { - this->addRecentlyOpenedFile(*path); + QString error; + QTextStream errorStream{&error}; + const bool succeeded = this->documents.saveModel(*modelId, errorStream); + if (not succeeded) + { + QMessageBox::critical(this, tr("Save error"), error); + } + else + { + this->addRecentlyOpenedFile(*path); + } } } } @@ -315,22 +323,25 @@ { if (this->currentDocument() != nullptr) { - const ModelId modelId = {0}; - const QString* pathPtr = this->documents.modelPath(modelId); - QString defaultPath = (pathPtr != nullptr) ? *pathPtr : ""; - const QString newPath = QFileDialog::getSaveFileName( - this, - tr("Save as…"), - QFileInfo{defaultPath}.absoluteDir().path(), - tr("LDraw files (*.ldr *dat);;All files (*)") - ); - if (not newPath.isEmpty()) + const std::optional<ModelId> modelId = this->findCurrentModelId(); + if (modelId.has_value()) { - QString error; - QTextStream errorStream{&error}; - this->documents.setModelPath(modelId, newPath, this->libraries, errorStream); - this->ui->tabs->setTabText(this->ui->tabs->currentIndex(), QFileInfo{newPath}.fileName()); - this->actionSave(); + const QString* pathPtr = this->documents.modelPath(*modelId); + QString defaultPath = (pathPtr != nullptr) ? *pathPtr : ""; + const QString newPath = QFileDialog::getSaveFileName( + this, + tr("Save as…"), + QFileInfo{defaultPath}.absoluteDir().path(), + tr("LDraw files (*.ldr *dat);;All files (*)") + ); + if (not newPath.isEmpty()) + { + QString error; + QTextStream errorStream{&error}; + this->documents.setModelPath(*modelId, newPath, this->libraries, errorStream); + this->ui->tabs->setTabText(this->ui->tabs->currentIndex(), QFileInfo{newPath}.fileName()); + this->actionSave(); + } } } } @@ -397,6 +408,19 @@ } } +std::optional<ModelId> MainWindow::findCurrentModelId() const +{ + const Document* document = this->currentDocument(); + if (document != nullptr) + { + return this->documents.findIdForModel(&document->getModel()); + } + else + { + return {}; + } +} + void MainWindow::changeEvent(QEvent* event) { if (event != nullptr)
--- a/src/mainwindow.h Wed Mar 09 14:22:22 2022 +0200 +++ b/src/mainwindow.h Sun Mar 13 14:51:39 2022 +0200 @@ -78,5 +78,7 @@ static QString pathToTranslation(const QString& localeCode); void loadColors(); Document *currentDocument(); + const Document *currentDocument() const; void closeDocument(Document* document); + std::optional<ModelId> findCurrentModelId() const; };
--- a/src/ui/objecteditor.cpp Wed Mar 09 14:22:22 2022 +0200 +++ b/src/ui/objecteditor.cpp Sun Mar 13 14:51:39 2022 +0200 @@ -1,15 +1,28 @@ #include <QVBoxLayout> +#include <QFormLayout> +#include <QPushButton> #include <QLabel> +#include <QSpinBox> #include "objecteditor.h" #include "document.h" +#include "modeleditor.h" +#include "widgets/colorbutton.h" +#include "widgets/colorindexinput.h" + +template<ldraw::Property property> +static void makeColorEditor() +{ + QString propertyName = ldraw::PropertyTraits<property>::name; +} ObjectEditor::ObjectEditor(Document* document, const ldraw::id_t id) : QWidget{document}, document{document}, + formContainer{new QWidget{this}}, objectTypeNameLabel{new QLabel{this}}, objectTypeIconLabel{new QLabel{this}} { - this->setObjectId(id); + this->polygonEditor.emplace(this->document, id); this->setLayout(new QVBoxLayout{this}); QWidget* objectTitleLayoutContainer = new QWidget{this}; QLayout* objectTitleLayout = new QHBoxLayout{objectTitleLayoutContainer}; @@ -18,6 +31,31 @@ objectTitleLayout->addWidget(this->objectTypeNameLabel); objectTitleLayout->addWidget(new QSplitter{Qt::Horizontal, this}); this->layout()->addWidget(objectTitleLayoutContainer); + this->layout()->addWidget(&*this->polygonEditor); + this->layout()->addWidget(formContainer); + this->setObjectId(id); + + QWidget* const parent = this->formContainer; + QFormLayout* formLayout = new QFormLayout{parent}; + this->formContainer->setLayout(formLayout); + QLabel* colorLabel = new QLabel{"Color", parent}; + ColorIndexInput* colorWidget = new ColorIndexInput{document, {0}, parent}; + formLayout->addRow(colorLabel, colorWidget); + connect(colorWidget, &ColorIndexInput::colorChanged, [this](ldraw::Color color) + { + const QModelIndex index = this->document->getModel().find(this->objectId); + if (index.isValid()) + { + this->document->editModel()->modifyObjectAt<ldraw::ColoredObject>( + index.row(), + [color](ldraw::ColoredObject* object) + { + object->colorIndex = color; + } + ); + } + }); + this->propertyWidgets[ldraw::Property::Color] = {colorLabel, colorWidget}; } QString titleCase(const QString& string) @@ -29,24 +67,42 @@ { this->objectId = id; const ldraw::Object* object = this->document->getModel().get(id); - if (object != nullptr and object->numPoints() > 0) + this->clear(); + if (object != nullptr) { this->objectTypeNameLabel->setText("<b>" + titleCase(object->typeName()) + "</b>"); this->objectTypeIconLabel->setPixmap(QPixmap{object->iconName()}.scaledToWidth(24)); - if (not this->polygonEditor.has_value()) + if (object->numPoints() > 0) { - this->polygonEditor.emplace(this->document, id); - this->layout()->addWidget(&*this->polygonEditor); + this->polygonEditor->setObjectId(id); } else { - this->polygonEditor->setObjectId(id); + this->polygonEditor->clear(); + } + constexpr ldraw::Property prop = ldraw::Property::Color; + QVariant color = object->getProperty(prop); + this->propertyWidgets[prop].first->setVisible(not color.isNull()); + this->propertyWidgets[prop].second->setVisible(not color.isNull()); + if (not color.isNull()) + { + static_cast<ColorIndexInput*>(this->propertyWidgets[prop].second)->setSelectedColor(color.value<ldraw::Color>()); } } else { - this->objectTypeNameLabel->clear(); - this->objectTypeIconLabel->clear(); - this->polygonEditor.reset(); + for (auto& pair : this->propertyWidgets) + { + pair.first->setVisible(false); + pair.second->setVisible(false); + } } } + +void ObjectEditor::clear() +{ + this->objectTypeNameLabel->clear(); + this->objectTypeIconLabel->clear(); + this->polygonEditor->clear(); + delete this->formContainer->layout(); +}
--- a/src/ui/objecteditor.h Wed Mar 09 14:22:22 2022 +0200 +++ b/src/ui/objecteditor.h Sun Mar 13 14:51:39 2022 +0200 @@ -12,10 +12,13 @@ public: explicit ObjectEditor(Document* document, ldraw::id_t id = ldraw::NULL_ID); void setObjectId(ldraw::id_t id); + void clear(); private: Document* const document; ldraw::id_t objectId = ldraw::NULL_ID; std::optional<PolygonObjectEditor> polygonEditor; + QWidget* formContainer; class QLabel* objectTypeNameLabel; class QLabel* objectTypeIconLabel; + QMap<ldraw::Property, QPair<QWidget*, QWidget*>> propertyWidgets; };
--- a/src/ui/polygonobjecteditor.cpp Wed Mar 09 14:22:22 2022 +0200 +++ b/src/ui/polygonobjecteditor.cpp Sun Mar 13 14:51:39 2022 +0200 @@ -14,6 +14,7 @@ storedObjectId{ldraw::NULL_ID.value} { this->splitter.emplace(Qt::Vertical, this); + this->buildWidgets(); this->setObjectId(id); } @@ -30,44 +31,60 @@ void PolygonObjectEditor::setObjectId(ldraw::id_t id) { this->storedObjectId = id; - this->buildWidgets(); + const QModelIndex index = this->document->getModel().find(this->objectId()); + if (index.isValid()) + { + for (int n : {0, 1, 2, 3}) + { + const ldraw::Object* const object = this->document->getModel()[index.row()]; + const ldraw::Property property = ldraw::pointProperty(n); + const QVariant value = object->getProperty(property); + this->widgets[n]->setVisible(value.isValid()); + this->formLayout->itemAt(n, QFormLayout::LabelRole)->widget()->setVisible(value.isValid()); + if (value.isValid()) + { + this->widgets[n]->setValue(value.value<glm::vec3>()); + } + } + } + else + { + for (int n : {0, 1, 2, 3}) + { + this->widgets[n]->setVisible(false); + this->formLayout->itemAt(n, QFormLayout::LabelRole)->widget()->setVisible(false); + } + } +} + +void PolygonObjectEditor::clear() +{ + this->setObjectId(ldraw::NULL_ID); } void PolygonObjectEditor::buildWidgets() { - this->widgets.clear(); - delete this->layout(); - QFormLayout* layout = new QFormLayout{this}; - this->setLayout(layout); + this->formLayout = new QFormLayout{this}; + this->setLayout(this->formLayout); for (int n : {0, 1, 2, 3}) { this->setupPointWidget(n); } - for (std::unique_ptr<QWidget>& widget : this->widgets) + for (std::unique_ptr<Vec3Editor>& widget : this->widgets) { const QString label = widget->property(LABEL_NAME).toString(); - layout->addRow(label, widget.get()); + this->formLayout->addRow(label, widget.get()); } - layout->addRow("", &*this->splitter); + this->formLayout->addRow("", &*this->splitter); } void PolygonObjectEditor::setupPointWidget(int n) { - const QModelIndex index = this->document->getModel().find(this->objectId()); - if (index.isValid()) - { - const ldraw::Object* const object = this->document->getModel()[index.row()]; - const ldraw::Property property = ldraw::pointProperty(n); - const QVariant value = object->getProperty(property); - if (value.isValid()) - { - std::unique_ptr<Vec3Editor> editor = std::make_unique<Vec3Editor>(value.value<glm::vec3>(), this); - QObject::connect(editor.get(), &Vec3Editor::valueChanged, this, &PolygonObjectEditor::pointChanged); - editor->setProperty(INDEX_NAME, QVariant::fromValue(n)); - editor->setProperty(LABEL_NAME, &ldraw::traits(property).name[0]); - this->widgets.push_back(std::move(editor)); - } - } + std::unique_ptr<Vec3Editor> editor = std::make_unique<Vec3Editor>(glm::vec3{}, this); + QObject::connect(editor.get(), &Vec3Editor::valueChanged, this, &PolygonObjectEditor::pointChanged); + editor->setProperty(INDEX_NAME, QVariant::fromValue(n)); + editor->setProperty(LABEL_NAME, &ldraw::traits(ldraw::pointProperty(n)).name[0]); + this->widgets.push_back(std::move(editor)); } void PolygonObjectEditor::pointChanged(const glm::vec3& value)
--- a/src/ui/polygonobjecteditor.h Wed Mar 09 14:22:22 2022 +0200 +++ b/src/ui/polygonobjecteditor.h Sun Mar 13 14:51:39 2022 +0200 @@ -13,12 +13,14 @@ ~PolygonObjectEditor(); ldraw::id_t objectId() const; void setObjectId(ldraw::id_t id); + void clear(); private: void buildWidgets(); void setupPointWidget(int n); Q_SLOT void pointChanged(const glm::vec3& value); Document* document; ldraw::id_t storedObjectId; - std::vector<std::unique_ptr<QWidget>> widgets; + std::vector<std::unique_ptr<Vec3Editor>> widgets; std::optional<QSplitter> splitter; + class QFormLayout* formLayout = nullptr; };
--- a/src/uiutilities.cpp Wed Mar 09 14:22:22 2022 +0200 +++ b/src/uiutilities.cpp Sun Mar 13 14:51:39 2022 +0200 @@ -16,6 +16,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <QStyleFactory> #include "uiutilities.h" QVector<QAction*> uiutilities::collectActions(QObject* subject) @@ -42,3 +43,12 @@ } return result; } + +void uiutilities::colorizeWidget(QWidget* widget, const QColor& color) +{ + QPalette pal{color}; + widget->setPalette(pal); + widget->setAutoFillBackground(true); + widget->setStyle(QStyleFactory::create("Fusion")); + widget->update(); +} \ No newline at end of file
--- a/src/uiutilities.h Wed Mar 09 14:22:22 2022 +0200 +++ b/src/uiutilities.h Sun Mar 13 14:51:39 2022 +0200 @@ -24,4 +24,5 @@ using KeySequenceMap = QMap<QString, QKeySequence>; QVector<QAction*> collectActions(QObject* subject); KeySequenceMap makeKeySequenceMap(const QVector<QAction*>& actions); + void colorizeWidget(QWidget* widget, const QColor& color); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/widgets/colorindexinput.cpp Sun Mar 13 14:51:39 2022 +0200 @@ -0,0 +1,43 @@ +#include "colorindexinput.h" +#include "ui_colorindexinput.h" +#include "colorselectdialog.h" +#include "uiutilities.h" + +ColorIndexInput::ColorIndexInput(Document *document, ldraw::Color color, QWidget *parent) : + QWidget{parent}, + document{document}, + ui{*new Ui_ColorIndexInput} +{ + this->ui.setupUi(this); + connect(this->ui.button, &QPushButton::clicked, [this]() + { + ColorSelectDialog dialog{this->document->colorTable, this->document}; + const int result = dialog.exec(); + if (result == QDialog::Accepted) + { + this->ui.index->setValue(dialog.currentColor().index); + } + }); + connect(this->ui.index, qOverload<int>(&QSpinBox::valueChanged), [this](int value) + { + this->ui.button->setText(ldraw::colorDisplayName({value}, this->document->colorTable)); + uiutilities::colorizeWidget(this->ui.button, ldraw::colorFace({value}, this->document->colorTable)); + Q_EMIT this->colorChanged({value}); + }); + this->ui.index->setValue(color.index); +} + +ColorIndexInput::~ColorIndexInput() +{ + delete &this->ui; +} + +ldraw::Color ColorIndexInput::selectedColor() const +{ + return {this->ui.index->value()}; +} + +void ColorIndexInput::setSelectedColor(ldraw::Color color) +{ + this->ui.index->setValue(color.index); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/widgets/colorindexinput.h Sun Mar 13 14:51:39 2022 +0200 @@ -0,0 +1,17 @@ +#pragma once +#include "document.h" + +class ColorIndexInput : public QWidget +{ + Q_OBJECT +public: + ColorIndexInput(Document *document, ldraw::Color color = ldraw::MAIN_COLOR, QWidget *parent = nullptr); + ~ColorIndexInput(); + ldraw::Color selectedColor() const; + void setSelectedColor(ldraw::Color color); +Q_SIGNALS: + void colorChanged(ldraw::Color color); +private: + Document* const document; + class Ui_ColorIndexInput& ui; +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/widgets/colorindexinput.ui Sun Mar 13 14:51:39 2022 +0200 @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ColorIndexInput</class> + <widget class="QWidget" name="ColorIndexInput"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>370</width> + <height>47</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QSpinBox" name="index"> + <property name="suffix"> + <string/> + </property> + <property name="maximum"> + <number>2147483647</number> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="button"> + <property name="text"> + <string>…</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui>
--- a/src/widgets/colorselectdialog.cpp Wed Mar 09 14:22:22 2022 +0200 +++ b/src/widgets/colorselectdialog.cpp Sun Mar 13 14:51:39 2022 +0200 @@ -1,7 +1,9 @@ #include <QColorDialog> #include <QTableView> +#include <QStyleFactory> #include "colorselectdialog.h" #include "ui_colorselectdialog.h" +#include "uiutilities.h" ColorSelectDialog::ColorSelectDialog(const ldraw::ColorTable& colorTable, QWidget *parent) : QDialog{parent}, @@ -29,14 +31,6 @@ return {button->property("_colorIndex").value<qint32>()}; } -QString styleSheetForColor(const QColor& color) -{ - QColor const textColor = (luma(color) < 0.4) ? Qt::white : Qt::black; - return QString{"background-color: %1; color: %2;"} - .arg(color.name()) - .arg(textColor.name()); -} - void ColorSelectDialog::makeColorButtons() { this->buttons.reserve(this->colorTable.size()); @@ -48,8 +42,9 @@ const qint32 index = iterator->first; const ldraw::ColorDefinition& colordef = iterator->second; QPushButton* const button = new QPushButton{QString::number(index), this}; + button->setMinimumSize({40, 40}); button->setToolTip(colordef.displayName); - button->setStyleSheet(styleSheetForColor(colordef.faceColor)); + uiutilities::colorizeWidget(button, ldraw::colorFace({index}, colorTable)); button->setProperty("_colorIndex", index); button->setCheckable(true); connect(button, &QAbstractButton::clicked, this, &ColorSelectDialog::handleButtonClick); @@ -95,17 +90,8 @@ void ColorSelectDialog::updateSelectedColorTexts() { - if (ldraw::isDirectColor(this->selectedColor)) - { - this->ui.selectedColorName->setText(ldraw::directColorFace(this->selectedColor).name()); - } - else - { - const ldraw::ColorDefinition& colordef = this->colorTable[this->selectedColor]; - this->ui.selectedColorName->setText(colordef.displayName); - } - const QColor colorFace = ldraw::colorFace(this->selectedColor, this->colorTable); - this->ui.selectedColorName->setStyleSheet(styleSheetForColor(colorFace)); + this->ui.selectedColorName->setText(ldraw::colorDisplayName(this->selectedColor, this->colorTable)); + uiutilities::colorizeWidget(this->ui.selectedColorName, ldraw::colorFace(this->selectedColor, colorTable)); this->ui.colorIndex->setValue(this->selectedColor.index); for (QPushButton* button : this->buttons) { @@ -157,3 +143,8 @@ this->selectedColor = color; this->updateSelectedColorTexts(); } + +ldraw::Color ColorSelectDialog::currentColor() const +{ + return this->selectedColor; +}