diff --git a/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj b/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj index a0e5563..91c9f62 100644 --- a/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj +++ b/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj @@ -153,7 +153,7 @@ - + @@ -189,7 +189,7 @@ - + @@ -197,6 +197,7 @@ + diff --git a/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj.filters b/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj.filters index e019bc3..71f1e04 100644 --- a/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj.filters +++ b/ArchitectureColoredPainting/ArchitectureColoredPainting.vcxproj.filters @@ -216,7 +216,7 @@ Source Files\Editor\Style - + Source Files\Editor\Style @@ -254,7 +254,7 @@ Header Files - + Header Files\Editor\Style @@ -477,6 +477,9 @@ Header Files\Editor\Style + + Header Files\Editor\util + diff --git a/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/StrokeStyleWidget.cpp b/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/StrokeStyleWidget.cpp new file mode 100644 index 0000000..00283ca --- /dev/null +++ b/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/StrokeStyleWidget.cpp @@ -0,0 +1,216 @@ +#include "StrokeStyleWidget.h" +#include "ColorPicker.h" +#include +#include + +#define radialStroke(stroke) std::dynamic_pointer_cast(stroke->materialStroke) +constexpr int COLUMN_WIDTH = 0; +constexpr int COLUMN_COLOR = 1; +constexpr int COLUMN_METALLIC = 2; +constexpr int COLUMN_ROUGHNESS = 3; +constexpr int COLUMN_OPERATIONS = 4; + +// FIXME: Material的控件有显示bug +StrokeStyleWidget::StrokeStyleWidget( + std::shared_ptr stroke, + QWidget* parent +) : QWidget(parent), stroke(stroke) +{ + QVBoxLayout* viewLayout = new QVBoxLayout(this); + this->setLayout(viewLayout); + + initStrokeSettings(); + QWidget* strokeProperties = new QWidget(this); + QHBoxLayout* strokePropertiesLayout = new QHBoxLayout(strokeProperties); + strokePropertiesLayout->setMargin(0); + + strokeProperties->setLayout(strokePropertiesLayout); + strokePropertiesLayout->addWidget(enableGradual); + strokePropertiesLayout->addWidget(endTypeBox); + + viewLayout->addWidget(strokeProperties); + viewLayout->addWidget(widthField); + + initTable(std::dynamic_pointer_cast(stroke->materialStroke)); + viewLayout->addWidget(strokeTable); + this->adjustSize(); +} + +void StrokeStyleWidget::initStrokeSettings() +{ + this->enableGradual = new QtMaterialCheckBox(this); + enableGradual->setText(QStringLiteral("启用渐变")); + enableGradual->setChecked(radialStroke(stroke)->gradual); + connect(enableGradual, &QtMaterialCheckBox::toggled, [this](bool checked) { + radialStroke(this->stroke)->gradual = checked; + }); + +#define endTypeBoxLabel(start, end) QStringLiteral(start##" -> "##end) + this->endTypeBox = new QComboBox(this); + endTypeBox->addItem(endTypeBoxLabel("圆头", "圆头")); // kRound + endTypeBox->addItem(endTypeBoxLabel("平头", "圆头")); // kFlatRound + endTypeBox->addItem(endTypeBoxLabel("圆头", "平头")); // kRoundFlat + endTypeBox->addItem(endTypeBoxLabel("平头", "平头")); // kFlat + endTypeBox->setCurrentIndex(static_cast(this->stroke->endType)); + connect(endTypeBox, QOverload::of(&QComboBox::currentIndexChanged), [this](int index) { + switch (index) + { + case 0: + this->stroke->endType = Renderer::StrokeEndType::kRound; + break; + case 1: + this->stroke->endType = Renderer::StrokeEndType::kFlatRound; + break; + case 2: + this->stroke->endType = Renderer::StrokeEndType::kRoundFlat; + break; + case 3: + this->stroke->endType = Renderer::StrokeEndType::kFlat; + break; + } + }); + + this->widthField = new QtMaterialTextField(this); + widthField->setLabel(QStringLiteral("本侧描边宽度")); + widthField->setText(QString::number(stroke->halfWidth)); + QDoubleValidator* widthValidator = new QDoubleValidator(0.1, std::numeric_limits::max(), 3, widthField); + widthValidator->setNotation(QDoubleValidator::StandardNotation); + widthField->setValidator(widthValidator); + connect(widthField, &QtMaterialTextField::textChanged, [this](const QString& changed) { + if (this->widthField->hasAcceptableInput()) + { + this->stroke->halfWidth = changed.toFloat(); + } + }); +} + +// TODO: 新增时参数校验 +void StrokeStyleWidget::initTable(std::shared_ptr materialStroke) +{ + this->strokeTable = new QTableWidget(this); + strokeTable->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContentsOnFirstShow); + strokeTable->setColumnCount(5); + strokeTable->setRowCount(materialStroke->materialMap.size() + 1); + QStringList headers; + headers << QStringLiteral("离心距离占比") + << QStringLiteral("颜色") + << QStringLiteral("金属度") + << QStringLiteral("粗糙度") + << QStringLiteral("其他操作"); + strokeTable->setHorizontalHeaderLabels(headers); + int row = 0; + // 内容 + for (auto & strokePair : materialStroke->materialMap) + { + setTableRow(row, strokePair.first, strokePair.second); + row++; + } + // 新增按钮 + QtMaterialRaisedButton* addButton = new QtMaterialRaisedButton("+", strokeTable); + strokeTable->setSpan(row, 0, 1, 5); + strokeTable->setCellWidget(row, 0, addButton); + connect(addButton, &QtMaterialRaisedButton::clicked, [this]() { + handlingRowInsert = true; + auto materialMap = &(radialStroke(this->stroke)->materialMap); + float newWidth = 0; + if (materialMap->size() == 0) + { + newWidth = 0.1; + } + else + { + auto lastPair = materialMap->rbegin(); + newWidth = lastPair->first + 0.01; + } + Renderer::Material newMaterial = { QColor::fromRgb(0,0,0), 0.f, .8f }; + (*materialMap)[newWidth] = newMaterial; + int newRow = this->strokeTable->rowCount() - 1; + this->strokeTable->insertRow(newRow); + setTableRow(newRow, newWidth, (*materialMap)[newWidth]); + this->strokeTable->update(); + handlingRowInsert = false; + }); + + connect(strokeTable, &QTableWidget::currentItemChanged, this, &StrokeStyleWidget::onCurrentItemChanged); + connect(strokeTable, &QTableWidget::cellChanged, this, &StrokeStyleWidget::onCellChanged); +} + +void StrokeStyleWidget::setTableRow(int row, float width, Renderer::Material& material) +{ + QTableWidgetItem* widthItem = new QTableWidgetItem; + widthItem->setData(Qt::EditRole, width); + strokeTable->setItem(row, COLUMN_WIDTH, widthItem); + + QColor* colorPtr = &(material.color); + QTableWidgetItem* colorItem = new QTableWidgetItem; + colorItem->setData(Qt::DisplayRole, *colorPtr); + strokeTable->setItem(row, COLUMN_COLOR, colorItem); + ColorPicker* colorPicker = new ColorPicker(*colorPtr, strokeTable); + strokeTable->setCellWidget(row, COLUMN_COLOR, colorPicker); + connect(colorPicker, &ColorPicker::colorChanged, [this, colorPtr](QColor color) { + *colorPtr = color; + this->strokeTable->update(); + }); + + QTableWidgetItem* metallicItem = new QTableWidgetItem; + metallicItem->setData(Qt::EditRole, material.metallic); + strokeTable->setItem(row, COLUMN_METALLIC, metallicItem); + + QTableWidgetItem* roughnessItem = new QTableWidgetItem; + roughnessItem->setData(Qt::EditRole, material.roughness); + strokeTable->setItem(row, COLUMN_ROUGHNESS, roughnessItem); + + QtMaterialRaisedButton* removeButton = new QtMaterialRaisedButton("-", strokeTable); + removeButton->setFixedSize(20, 20); + strokeTable->setCellWidget(row, COLUMN_OPERATIONS, removeButton); + connect(removeButton, &QtMaterialRaisedButton::clicked, [this, row]() { + if (this->strokeTable->rowCount() <= 1) return; + radialStroke(this->stroke)->materialMap.erase(this->strokeTable->item(row, COLUMN_WIDTH)->text().toFloat()); + this->strokeTable->removeRow(row); + }); +} + +void StrokeStyleWidget::onCurrentItemChanged(QTableWidgetItem* current, QTableWidgetItem* previous) +{ + if (!current) return; + int column = current->column(); + if (column != COLUMN_COLOR && column != COLUMN_OPERATIONS) + { + this->currentItemValue = current->data(Qt::EditRole); + } +} + +void StrokeStyleWidget::onCellChanged(int row, int column) +{ + if (handlingRowInsert) return; + auto changedItem = strokeTable->item(row, column); + auto changedItemValue = changedItem->text().toFloat(); + if (changedItemValue < 0 || 1 < changedItemValue) + { + changedItem->setData(Qt::EditRole, this->currentItemValue.toFloat()); + return; + } + auto changedWidth = strokeTable->item(row, COLUMN_WIDTH)->data(Qt::EditRole).toFloat(); + switch (column) + { + case COLUMN_WIDTH: + { + float oldWidth = this->currentItemValue.toFloat(); + auto node = radialStroke(stroke)->materialMap.extract(oldWidth); + node.key() = changedWidth; + radialStroke(stroke)->materialMap.insert(std::move(node)); + strokeTable->sortItems(COLUMN_WIDTH); + break; + } + case COLUMN_METALLIC: + { + radialStroke(stroke)->materialMap[changedWidth].metallic = changedItemValue; + break; + } + case COLUMN_ROUGHNESS: + { + radialStroke(stroke)->materialMap[changedWidth].roughness = changedItemValue; + break; + } + } +} \ No newline at end of file diff --git a/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/StrokeStyleWidget.h b/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/StrokeStyleWidget.h new file mode 100644 index 0000000..6d5dbdd --- /dev/null +++ b/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/StrokeStyleWidget.h @@ -0,0 +1,33 @@ +#pragma once +#include "LayerStyle.h" +#include "../Renderer/Painting/MaterialStyleStroke.h" +#include +#include +#include +#include +#include +class StrokeStyleWidget : public QWidget +{ + Q_OBJECT +private: + QVariant currentItemValue; + + QtMaterialCheckBox* enableGradual; + QComboBox* endTypeBox; + QtMaterialTextField* widthField; + QTableWidget* strokeTable; + bool handlingRowInsert = false; + + void initStrokeSettings(); + void initTable(std::shared_ptr materialStroke); + void setTableRow(int row, float width, Renderer::Material& material); + +public: + StrokeStyleWidget(std::shared_ptr stroke, QWidget* parent = nullptr); + std::shared_ptr stroke; + +protected slots: + void onCurrentItemChanged(QTableWidgetItem* current, QTableWidgetItem* previous); + void onCellChanged(int row, int column); +}; + diff --git a/ArchitectureColoredPainting/src/Editor/LayerStyle.cpp b/ArchitectureColoredPainting/src/Editor/LayerStyle.cpp index e44e855..9b65355 100644 --- a/ArchitectureColoredPainting/src/Editor/LayerStyle.cpp +++ b/ArchitectureColoredPainting/src/Editor/LayerStyle.cpp @@ -1,5 +1,6 @@ #include "LayerStyle.h" -#include "./EditorWidgetComponent/StrokeStyleListView.h" +#include "./EditorWidgetComponent/StrokeStyleWidget.h" +#include "./util/JsonUtil.hpp" #include #include #include @@ -50,8 +51,7 @@ QWidget* StrokeElementLayerStyle::getInputWidget() if (this->strokePair.first == nullptr) { auto materialMap = std::map(); - materialMap[0.3] = Renderer::Material{ QColor(0,255,255), 0.f, .8f }; - materialMap[1.0] = Renderer::Material{ QColor(80,25,255), 0.f, .8f }; + materialMap[1.0] = Renderer::Material{ QColor(0, 0, 0), 0.f, .8f }; this->strokePair.first = std::shared_ptr(new Renderer::MaterialStyleStroke( 15, Renderer::StrokeType::kLeftSide, Renderer::StrokeEndType::kFlat, @@ -63,8 +63,7 @@ QWidget* StrokeElementLayerStyle::getInputWidget() if (this->strokePair.second == nullptr) { auto materialMap = std::map(); - materialMap[0.3] = Renderer::Material{ QColor(0,255,255), 0.f, .8f }; - materialMap[1.0] = Renderer::Material{ QColor(80,25,255), 0.f, .8f }; + materialMap[1.0] = Renderer::Material{ QColor(0, 0, 0), 0.f, .8f }; this->strokePair.second = std::shared_ptr(new Renderer::MaterialStyleStroke( 15, Renderer::StrokeType::kRightSide, Renderer::StrokeEndType::kFlat, @@ -79,9 +78,7 @@ QWidget* StrokeElementLayerStyle::getInputWidget() QVBoxLayout* layout = new QVBoxLayout(w); layout->setMargin(0); - StrokeStyleListView* leftStrokeView = new StrokeStyleListView( - std::dynamic_pointer_cast(this->strokePair.first->materialStroke), w - ); + StrokeStyleWidget* leftStrokeView = new StrokeStyleWidget(this->strokePair.first, w); layout->addWidget(leftStrokeView); QtMaterialCheckBox* checkEachSideIndependent = new QtMaterialCheckBox(w); @@ -89,9 +86,7 @@ QWidget* StrokeElementLayerStyle::getInputWidget() checkEachSideIndependent->setChecked(enableEachSideIndependent); layout->addWidget(checkEachSideIndependent); - StrokeStyleListView* rightStrokeView = new StrokeStyleListView( - std::dynamic_pointer_cast(this->strokePair.second->materialStroke), w - ); + StrokeStyleWidget* rightStrokeView = new StrokeStyleWidget(this->strokePair.second, w); layout->addWidget(rightStrokeView); rightStrokeView->setDisabled(!this->enableEachSideIndependent); @@ -124,6 +119,16 @@ StrokeElementLayerStyle::StrokeElementLayerStyle(const StrokeElementLayerStyle& enableEachSideIndependent = other.enableEachSideIndependent; } +QJsonObject StrokeElementLayerStyle::toJson() const +{ + QJsonObject json; + json.insert("type", "stroke"); + json.insert("enableEachSideIndependent", enableEachSideIndependent); + json.insert("left", JsonUtil::toQJsonArray(strokePair.first->encoded())); + json.insert("right", JsonUtil::toQJsonArray(strokePair.second->encoded())); + return json; +} + std::unique_ptr StrokeElementLayerStyle::clone() const { return std::make_unique(StrokeElementLayerStyle(*this)); @@ -167,7 +172,17 @@ FillElementLayerStyle::FillElementLayerStyle(const FillElementLayerStyle& other) } } +QJsonObject FillElementLayerStyle::toJson() const +{ + return QJsonObject(); +} + std::unique_ptr FillElementLayerStyle::clone() const { return std::make_unique(FillElementLayerStyle(*this)); } + +std::unique_ptr LayerStyle::fromJson(const QJsonObject& json) +{ + return std::unique_ptr(); +} diff --git a/ArchitectureColoredPainting/src/Editor/LayerStyle.h b/ArchitectureColoredPainting/src/Editor/LayerStyle.h index 7d5fb25..6717299 100644 --- a/ArchitectureColoredPainting/src/Editor/LayerStyle.h +++ b/ArchitectureColoredPainting/src/Editor/LayerStyle.h @@ -5,6 +5,7 @@ #include #include #include +#include #include "../Renderer/Painting/ElementStyle.h" #include "../Renderer/Painting/MaterialStyleStroke.h" #include "../Renderer/Painting/MaterialStyleFill.h" @@ -23,10 +24,13 @@ class LayerStyle : public Renderer::ElementStyle { public: const static std::vector()>>> types; + static std::unique_ptr fromJson(const QJsonObject& json); + virtual QString getStyleName() const = 0; virtual QWidget* getInputWidget() = 0; virtual QWidget* getListDisplayWidget() const = 0; virtual ~LayerStyle() {}; + virtual QJsonObject toJson() const = 0; virtual std::unique_ptr clone() const = 0; }; @@ -43,6 +47,7 @@ public: StrokeElementLayerStyle() = default; StrokeElementLayerStyle(const StrokeElementLayerStyle& other); ~StrokeElementLayerStyle() = default; + QJsonObject toJson() const override; std::unique_ptr clone() const override; bool enableEachSideIndependent = false; @@ -59,5 +64,6 @@ public: FillElementLayerStyle(const FillElementLayerStyle& other); ~FillElementLayerStyle() = default; std::vector> materialStyles; + QJsonObject toJson() const override; std::unique_ptr clone() const override; }; \ No newline at end of file diff --git a/ArchitectureColoredPainting/src/Editor/LayerWrapper.cpp b/ArchitectureColoredPainting/src/Editor/LayerWrapper.cpp index 4c73d5d..0e9b8f4 100644 --- a/ArchitectureColoredPainting/src/Editor/LayerWrapper.cpp +++ b/ArchitectureColoredPainting/src/Editor/LayerWrapper.cpp @@ -240,6 +240,10 @@ QJsonObject LeafLayerWrapper::toJson() const QJsonObject json = LayerWrapper::toJson(); json.insert("element", wrappedElement->index); json.insert("is-folder", false); + QJsonArray stylesJson; + for (auto& style : styles) + stylesJson.push_back(style->toJson()); + json.insert("styles", stylesJson); return json; } diff --git a/ArchitectureColoredPainting/src/Editor/LayerWrapper.h b/ArchitectureColoredPainting/src/Editor/LayerWrapper.h index 7df5d13..f753dcb 100644 --- a/ArchitectureColoredPainting/src/Editor/LayerWrapper.h +++ b/ArchitectureColoredPainting/src/Editor/LayerWrapper.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include diff --git a/ArchitectureColoredPainting/src/Editor/util/JsonUtil.hpp b/ArchitectureColoredPainting/src/Editor/util/JsonUtil.hpp new file mode 100644 index 0000000..e393f38 --- /dev/null +++ b/ArchitectureColoredPainting/src/Editor/util/JsonUtil.hpp @@ -0,0 +1,14 @@ +#pragma once +#include + +namespace JsonUtil +{ +#include + template + QJsonArray toQJsonArray(const std::vector& vec) + { + QJsonArray array; + std::copy(vec.begin(), vec.end(), std::back_inserter(array)); + return array; + } +}