diff --git a/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/LayerContainerListWidget.cpp b/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/LayerContainerListWidget.cpp index c28a7e1..9720e20 100644 --- a/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/LayerContainerListWidget.cpp +++ b/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/LayerContainerListWidget.cpp @@ -1,7 +1,12 @@ #include "LayerContainerListWidget.h" + #include "LayerStyleDialog.h" #include "../ColorHelper.hpp" #include +#include +#include +#include +#include #include inline void initMaterialButton(QtMaterialRaisedButton* button) @@ -13,11 +18,6 @@ inline void initMaterialButton(QtMaterialRaisedButton* button) LayerContainerListWidget::LayerContainerListWidget(QWidget* parent, const PLayerStyleContainer& styleContainer) : QWidget(parent), headerWidget(new QWidget(this)), styleList(new QListWidget(this)), styleContainer(styleContainer) { - connect(this, &LayerContainerListWidget::addLayerStyle, - this, &LayerContainerListWidget::onAddLayerStyle); - connect(this, &LayerContainerListWidget::removeLayerStyle, - this, &LayerContainerListWidget::onRemoveLayerStyle); - auto* widgetLayout = new QVBoxLayout(this); initHeader(); widgetLayout->addWidget(this->headerWidget); @@ -43,6 +43,7 @@ void LayerContainerListWidget::setStyleContainer(const PLayerStyleContainer& sty { if (!forceRefresh && comparePStyleContainersEquality(this->styleContainer, styleContainer)) { + this->styleContainer = styleContainer; return; } @@ -52,7 +53,6 @@ void LayerContainerListWidget::setStyleContainer(const PLayerStyleContainer& sty { return; } - this->styleContainer = styleContainer; for (const auto& style : *styleContainer | std::views::values) @@ -60,6 +60,7 @@ void LayerContainerListWidget::setStyleContainer(const PLayerStyleContainer& sty auto* item = new QListWidgetItem(styleList); item->setSizeHint(QSize(50, 40)); styleList->setItemWidget(item, buildStyleListWidget(style)); + styleItemMap[style->getDisplayName()] = item; } resetAddButton(); } @@ -75,11 +76,10 @@ void LayerContainerListWidget::resetAddButton() { return; } - qDebug() << "resetAddButton" << styleContainer->full(); addButton->setDisabled(styleContainer->full()); } -void LayerContainerListWidget::onAddLayerStyle(const std::shared_ptr& style) +void LayerContainerListWidget::addLayerStyle(const std::shared_ptr& style) { if (const bool ok = styleContainer->useStyle(style)) { @@ -89,10 +89,18 @@ void LayerContainerListWidget::onAddLayerStyle(const std::shared_ptr styleItemMap[style->getDisplayName()] = newItem; newItem->setSizeHint(QSize(50, 40)); styleList->setItemWidget(newItem, buildStyleListWidget(style)); + emit refreshStylePreview(); } } -void LayerContainerListWidget::onRemoveLayerStyle(const QString& styleName) +void LayerContainerListWidget::editLayerStyle(const std::shared_ptr& style) +{ + this->styleContainer->overrideStyle(style); + this->styleContainer->computeNewHash(); + emit refreshStylePreview(); +} + +void LayerContainerListWidget::removeLayerStyle(const QString& styleName) { if (const bool ok = styleContainer->dropStyle(styleName)) { @@ -100,9 +108,35 @@ void LayerContainerListWidget::onRemoveLayerStyle(const QString& styleName) auto* removedItem = styleItemMap.extract(styleName).mapped(); resetAddButton(); delete styleList->takeItem(styleList->row(removedItem)); + emit refreshStylePreview(); } } +void LayerContainerListWidget::copyStyles() const +{ + QApplication::clipboard()->setText(QJsonDocument(styleContainer->toJson()).toJson()); +} + +void LayerContainerListWidget::pasteStyles() +{ + QJsonParseError jsonError; + const auto& document = QJsonDocument::fromJson(QApplication::clipboard()->text().toUtf8(), &jsonError); + if (jsonError.error || !document.isArray()) + { + QMessageBox::critical(this, QStringLiteral("错误!"), QStringLiteral("剪贴板中的文本无法被识别为样式。")); + return; + } + const auto& jsonArray = document.array(); + if (jsonArray.empty()) + { + return; + } + const auto& container = LayerStyleContainer::fromJson(ElementType::TYPE_ALL, jsonArray); + *this->styleContainer << container; + setStyleContainer(this->styleContainer, true); + emit refreshStylePreview(); +} + void LayerContainerListWidget::initHeader() { auto* headerLayout = new QHBoxLayout; @@ -118,16 +152,17 @@ void LayerContainerListWidget::initHeader() dialog->exec(); if (dialog->layerStyle) { - emit addLayerStyle(dialog->layerStyle); + addLayerStyle(dialog->layerStyle); } }); this->copyButton = new QtMaterialRaisedButton(QStringLiteral("复制所有样式"), headerWidget); initMaterialButton(copyButton); - // TODO: 实现复制 + connect(copyButton, &QtMaterialRaisedButton::clicked, this, &LayerContainerListWidget::copyStyles); this->pasteButton = new QtMaterialRaisedButton(QStringLiteral("从剪贴板粘贴"), headerWidget); initMaterialButton(pasteButton); + connect(pasteButton, &QtMaterialRaisedButton::clicked, this, &LayerContainerListWidget::pasteStyles); headerLayout->addWidget(headerLabel); headerLayout->addWidget(addButton); @@ -135,11 +170,6 @@ void LayerContainerListWidget::initHeader() headerLayout->addWidget(pasteButton); headerLayout->setContentsMargins(5, 0, 5, 0); headerWidget->setLayout(headerLayout); - - /*auto* headerItem = new QListWidgetItem(this); - headerItem->setFlags(Qt::NoItemFlags); - this->addItem(headerItem); - this->setItemWidget(headerItem, headerWidget);*/ } @@ -167,17 +197,12 @@ QWidget* LayerContainerListWidget::buildStyleListWidget(std::shared_ptrlayerStyle) { - this->styleContainer->overrideStyle(dialog->layerStyle); - this->styleContainer->computeNewHash(); - emit editLayerStyle(targetStyle); + editLayerStyle(dialog->layerStyle); } }); connect(removeButton, &QPushButton::clicked, - [this, styleDisplayName] - { - emit removeLayerStyle(styleDisplayName); - }); + [this, styleDisplayName] { removeLayerStyle(styleDisplayName); }); QWidget* styleDisplayWidget = style->getListDisplayWidget(); styleDisplayWidget->setParent(w); diff --git a/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/LayerContainerListWidget.h b/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/LayerContainerListWidget.h index 819e747..4e0a76a 100644 --- a/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/LayerContainerListWidget.h +++ b/ArchitectureColoredPainting/src/Editor/EditorWidgetComponent/LayerContainerListWidget.h @@ -26,13 +26,15 @@ public: void setStyleContainer(const PLayerStyleContainer& styleContainer, bool forceRefresh = false); PLayerStyleContainer getStyleContainer() const; -protected slots: - void onAddLayerStyle(const std::shared_ptr& style); - void onRemoveLayerStyle(const QString& styleName); - -signals: void addLayerStyle(const std::shared_ptr& style); void editLayerStyle(const std::shared_ptr& style); void removeLayerStyle(const QString& styleName); + +protected slots: + void copyStyles() const; + void pasteStyles(); + +signals: + void refreshStylePreview(); }; diff --git a/ArchitectureColoredPainting/src/Editor/LayerStyle.cpp b/ArchitectureColoredPainting/src/Editor/LayerStyle.cpp index fbf5486..532adac 100644 --- a/ArchitectureColoredPainting/src/Editor/LayerStyle.cpp +++ b/ArchitectureColoredPainting/src/Editor/LayerStyle.cpp @@ -92,7 +92,7 @@ void LayerStyleContainer::computeNewHash() } } -LayerStyleContainer LayerStyleContainer::fromJson(ElementType elementType, const QJsonArray& jsonArray) +LayerStyleContainer LayerStyleContainer::fromJson(ElementType::ElementType elementType, const QJsonArray& jsonArray) { LayerStyleContainer container(elementType); for (const auto& style : jsonArray) @@ -102,26 +102,26 @@ LayerStyleContainer LayerStyleContainer::fromJson(ElementType elementType, const return container; } -LayerStyleContainer::LayerStyleContainer(ElementType elementType) : hash(0) +LayerStyleContainer::LayerStyleContainer(ElementType::ElementType elementType) : elementType(elementType), hash(0) { for (const auto& style : commonStyles) { unusedStyles.insert(style); } - if (elementType & LayerStyleContainer::TYPE_CLOSED) + if (elementType & ElementType::TYPE_UNCLOSED) + { + for (const auto& style : unclosedOnlyStyles) + { + unusedStyles.insert(style); + } + } + if (elementType & ElementType::TYPE_CLOSED) { for (const auto& style : closedOnlyStyles) { unusedStyles.insert(style); } } - if (elementType & LayerStyleContainer::TYPE_UNCLOSED) - { - for (const auto& style : unclosedOnlyStyles) - { - unusedStyles.insert(style); - } - } } std::vector LayerStyleContainer::toBaseStyles() const @@ -196,27 +196,32 @@ bool LayerStyleContainer::useStyle(const std::shared_ptr& style, boo { const auto styleDisplayName = style->getDisplayName(); auto styleNode = unusedStyles.extract(styleDisplayName); - if (styleNode.empty()) + if (styleNode.empty() && !forceOverride) { - if (!forceOverride || !styles.contains(styleDisplayName)) - { - return false; - } - styles[styleDisplayName] = style; - return true; + return false; } - styles[styleNode.key()] = style; - usedStyles.insert(std::move(styleNode)); + auto uFittedStyle = style->fitForElementType(this->elementType); + auto fittedStyle = std::shared_ptr(std::move(uFittedStyle)); + if (!fittedStyle) + { + return false; + } + if (!styleNode.empty()) + { + usedStyles.insert(std::move(styleNode)); + } + styles[styleDisplayName] = fittedStyle; return true; } bool LayerStyleContainer::overrideStyle(const std::shared_ptr& style) { - if (!styles.contains(style->getDisplayName())) + auto fittedStyle = std::shared_ptr(std::move(style->fitForElementType(this->elementType))); + if (!fittedStyle) { return false; } - styles[style->getDisplayName()] = style; + styles[style->getDisplayName()] = fittedStyle; return true; } @@ -287,6 +292,17 @@ LayerStyleContainer LayerStyleContainer::operator|(const LayerStyleContainer& ot return result; } +LayerStyleContainer& LayerStyleContainer::operator<<(const LayerStyleContainer& other) +{ + for (auto& style : std::ranges::subrange(other.cbegin(), other.cend()) + | std::views::values) + { + this->useStyle(style, true); + } + computeNewHash(); + return *this; +} + std::unique_ptr StrokeElementLayerStyle::fromJson(const QJsonObject& json) { auto ptr = std::make_unique( @@ -351,6 +367,28 @@ std::unique_ptr StrokeElementLayerStyle::clone() const return std::make_unique(StrokeElementLayerStyle(*this)); } +std::unique_ptr StrokeElementLayerStyle::fitForElementType(ElementType::ElementType type) const +{ + auto result = std::unique_ptr(reinterpret_cast(this->clone().release())); + const bool isThisClosed = strokePair.first->endType == Renderer::StrokeEndType::kClosed; + const bool isRequiredTypeClosed = (type & ElementType::TYPE_CLOSED) == ElementType::TYPE_CLOSED; + if (isThisClosed == isRequiredTypeClosed) + { + return result; + } + if (type == ElementType::TYPE_CLOSED) + { + result->strokePair.first->endType = Renderer::StrokeEndType::kClosed; + result->strokePair.second->endType = Renderer::StrokeEndType::kClosed; + } + else if (isThisClosed) + { + result->strokePair.first->endType = Renderer::StrokeEndType::kFlat; + result->strokePair.second->endType = Renderer::StrokeEndType::kFlat; + } + return result; +} + bool StrokeElementLayerStyle::operator==(const LayerStyle& other) const { if (!LayerStyle::operator==(other)) @@ -426,6 +464,15 @@ std::unique_ptr FillElementLayerStyle::clone() const return std::make_unique(FillElementLayerStyle(*this)); } +std::unique_ptr FillElementLayerStyle::fitForElementType(ElementType::ElementType type) const +{ + if (type & ElementType::TYPE_CLOSED) + { + return this->clone(); + } + return nullptr; +} + bool FillElementLayerStyle::operator==(const LayerStyle& other) const { if (!LayerStyle::operator==(other)) diff --git a/ArchitectureColoredPainting/src/Editor/LayerStyle.h b/ArchitectureColoredPainting/src/Editor/LayerStyle.h index 5dfa36c..3e29ae5 100644 --- a/ArchitectureColoredPainting/src/Editor/LayerStyle.h +++ b/ArchitectureColoredPainting/src/Editor/LayerStyle.h @@ -21,6 +21,14 @@ using Renderer::MaterialStyleFill; #define radialStroke(stroke) std::static_pointer_cast((stroke)->materialStroke) #define plainFill(fill) std::static_pointer_cast((fill)->materialFill) +namespace ElementType +{ + using ElementType = unsigned short; + constexpr ElementType TYPE_ALL = 0xffff; + constexpr ElementType TYPE_CLOSED = 0b01; + constexpr ElementType TYPE_UNCLOSED = 0b10; +}; + class LayerStyle : public Renderer::ElementStyle { public: @@ -35,6 +43,7 @@ public: virtual QJsonObject toJson() const; virtual std::unique_ptr clone() const = 0; + virtual std::unique_ptr fitForElementType(ElementType::ElementType type) const = 0; virtual bool operator==(const LayerStyle& other) const; }; @@ -57,6 +66,7 @@ public: QWidget* getListDisplayWidget() const override; QJsonObject toJson() const override; std::unique_ptr clone() const override; + std::unique_ptr fitForElementType(ElementType::ElementType type) const override; bool operator==(const LayerStyle& other) const override; @@ -81,6 +91,7 @@ public: QWidget* getListDisplayWidget() const override; QJsonObject toJson() const override; std::unique_ptr clone() const override; + std::unique_ptr fitForElementType(ElementType::ElementType type) const override; bool operator==(const LayerStyle& other) const override; @@ -111,19 +122,15 @@ private: [] { return std::make_unique(false); } } }; + ElementType::ElementType elementType; DisplayNameWithSupplier unusedStyles; DisplayNameWithSupplier usedStyles; std::map> styles; size_t hash; public: - using ElementType = unsigned short; - constexpr static ElementType TYPE_ALL = 0xffff; - constexpr static ElementType TYPE_CLOSED = 0b01; - constexpr static ElementType TYPE_UNCLOSED = 0b10; + static LayerStyleContainer fromJson(ElementType::ElementType elementType, const QJsonArray& jsonArray); - static LayerStyleContainer fromJson(ElementType elementType, const QJsonArray& jsonArray); - - LayerStyleContainer(ElementType elementType); + LayerStyleContainer(ElementType::ElementType elementType); [[nodiscard]] std::vector toBaseStyles() const override; [[nodiscard]] QJsonArray toJson() const; @@ -148,6 +155,7 @@ public: * 类管道操作,后者覆盖前者,会返回一个新的LayerStyleContainer */ [[nodiscard]] LayerStyleContainer operator|(const LayerStyleContainer& other) const; + LayerStyleContainer& operator<<(const LayerStyleContainer& other); /** * 需要在每次更改后手动调用 diff --git a/ArchitectureColoredPainting/src/Editor/LayerWrapper.cpp b/ArchitectureColoredPainting/src/Editor/LayerWrapper.cpp index a0ca73f..22dc6e1 100644 --- a/ArchitectureColoredPainting/src/Editor/LayerWrapper.cpp +++ b/ArchitectureColoredPainting/src/Editor/LayerWrapper.cpp @@ -59,7 +59,7 @@ FolderLayerWrapper::FolderLayerWrapper(QJsonObject json, ElementManager *element qDebug() << json.value("name").toString() << " " << this; QJsonArray childrenJson = json.value("children").toArray(); QJsonValue referencedJson = json.value("referenced-by"); - styles = new LayerStyleContainer(LayerStyleContainer::TYPE_ALL); + styles = new LayerStyleContainer(ElementType::TYPE_ALL); if (!referencedJson.isNull()) { auto p = reinterpret_cast(elementManager->getElementById(referencedJson.toInt())); @@ -83,7 +83,7 @@ LeafLayerWrapper::LeafLayerWrapper(QJsonObject json, ElementManager* elementMana wrappedElement(elementManager->getElementById(json.value("element").toInt())) { styles = new LayerStyleContainer(LayerStyleContainer::fromJson( - wrappedElement->isClosed() ? LayerStyleContainer::TYPE_CLOSED : LayerStyleContainer::TYPE_UNCLOSED, + wrappedElement->isClosed() ? ElementType::TYPE_CLOSED : ElementType::TYPE_UNCLOSED, json.value("styles").toArray())); qDebug() << json.value("name").toString() << " " << this; if(wrappedElement != nullptr) diff --git a/ArchitectureColoredPainting/src/Editor/LayerWrapper.h b/ArchitectureColoredPainting/src/Editor/LayerWrapper.h index 52b3de3..fe3dacc 100644 --- a/ArchitectureColoredPainting/src/Editor/LayerWrapper.h +++ b/ArchitectureColoredPainting/src/Editor/LayerWrapper.h @@ -62,7 +62,7 @@ class LayerWrapper FolderLayerWrapper*getParent() const; // invoke by manager, then invoke parent's applyStyles LayerWrapper(QJsonObject json, FolderLayerWrapper*parent, ElementManager* elementManager=nullptr); LayerWrapper() = default; - virtual void paint(QPainter* painter, QTransform transform = QTransform(), bool force = false, LayerStyleContainer styles = LayerStyleContainer(LayerStyleContainer::TYPE_ALL)); + virtual void paint(QPainter* painter, QTransform transform = QTransform(), bool force = false, LayerStyleContainer styles = LayerStyleContainer(ElementType::TYPE_ALL)); // TODO : export Function // virtual LayerWrapper *addChild() = 0; // Leaf Child Only // virtual LayerWrapper *addParent() = 0; // Folder Parent Only @@ -103,7 +103,7 @@ class FolderLayerWrapper : public LayerWrapper QTreeWidgetItem* getQTreeItem() override; QJsonObject toJson() const override; int getReferencedBy()const; - void paint(QPainter* painter, QTransform transform = QTransform(), bool force = false, LayerStyleContainer styles = LayerStyleContainer(LayerStyleContainer::TYPE_ALL)) override; + void paint(QPainter* painter, QTransform transform = QTransform(), bool force = false, LayerStyleContainer styles = LayerStyleContainer(ElementType::TYPE_ALL)) override; void collectDownReachable(std::set& reachable) override; void refreshTreeItem() override; size_t referencedCount(bool excludeSelf = false) const override; @@ -121,7 +121,7 @@ class LeafLayerWrapper : public LayerWrapper void refresh(LayerWrapper* layer = nullptr) override; LeafLayerWrapper(QJsonObject json, ElementManager *elementManager, FolderLayerWrapper*parent); QJsonObject toJson() const override; - void paint(QPainter* painter, QTransform transform = QTransform(), bool force = false, LayerStyleContainer styles = LayerStyleContainer(LayerStyleContainer::TYPE_ALL)) override; + void paint(QPainter* painter, QTransform transform = QTransform(), bool force = false, LayerStyleContainer styles = LayerStyleContainer(ElementType::TYPE_ALL)) override; void collectDownReachable(std::set& reachable) override; QTreeWidgetItem* getQTreeItem() override; void refreshTreeItem() override; diff --git a/ArchitectureColoredPainting/src/Editor/RightBar/InfoDisplayWidget.cpp b/ArchitectureColoredPainting/src/Editor/RightBar/InfoDisplayWidget.cpp index 91b1837..a13cdac 100644 --- a/ArchitectureColoredPainting/src/Editor/RightBar/InfoDisplayWidget.cpp +++ b/ArchitectureColoredPainting/src/Editor/RightBar/InfoDisplayWidget.cpp @@ -23,7 +23,7 @@ InfoDisplayWidget::InfoDisplayWidget(QWidget* parent) :QWidget(parent) ui.scaleX->setLabel(("姘村钩缂╂斁")); ui.scaleY->setLabel(("鍨傜洿缂╂斁")); ui.rotation->setValidator(new QIntValidator(-360, 360, this)); - ui.styleList->setDisabled(true); + //ui.styleList->setDisabled(true); connect(ui.rotation, &QLineEdit::textChanged, [=](const QString& content) { if (fabs(content.toDouble() - this->displayLayer->property.rotation) < 1e-6) return; @@ -70,13 +70,7 @@ InfoDisplayWidget::InfoDisplayWidget(QWidget* parent) :QWidget(parent) this->displayLayer->property.flipY = state; emit triggerCentralRefresh(); }); - connect(ui.styleList, &LayerContainerListWidget::addLayerStyle, [this](const std::shared_ptr& style) { - emit triggerCentralRefresh(); - }); - connect(ui.styleList, &LayerContainerListWidget::editLayerStyle, [this](const std::shared_ptr& style) { - emit triggerCentralRefresh(); - }); - connect(ui.styleList, &LayerContainerListWidget::removeLayerStyle, [this](const QString& styleName) { + connect(ui.styleList, &LayerContainerListWidget::refreshStylePreview, [this] { emit triggerCentralRefresh(); }); } @@ -99,14 +93,15 @@ void InfoDisplayWidget::refresh() ui.scaleY->setText(QString::number(this->displayLayer->property.scale.y())); ui.flipX->setChecked(this->displayLayer->property.flipX); ui.flipY->setChecked(this->displayLayer->property.flipY); - if (this->displayLayer->canApplyStyles()) + /*if (this->displayLayer->canApplyStyles()) { ui.styleList->setDisabled(false); - ui.styleList->setStyleContainer(static_cast(this->displayLayer)->styles); + ui.styleList->setStyleContainer(this->displayLayer->styles); } else { ui.styleList->setDisabled(true); - } + }*/ + ui.styleList->setStyleContainer(this->displayLayer->styles); } } \ No newline at end of file diff --git a/UnitTest/LayerStyleTest.cpp b/UnitTest/LayerStyleTest.cpp index 5b76166..ca8813e 100644 --- a/UnitTest/LayerStyleTest.cpp +++ b/UnitTest/LayerStyleTest.cpp @@ -16,8 +16,8 @@ namespace UnitTest public: LayerStyleContainerTest() : argv{ const_cast("") }, argc(1), - containerParent(LayerStyleContainer::TYPE_ALL), - containerChild(LayerStyleContainer::TYPE_UNCLOSED) + containerParent(ElementType::TYPE_ALL), + containerChild(ElementType::TYPE_UNCLOSED) { QApplication app(argc, argv); Assert::IsTrue(containerParent.empty());