[style] 修改了LayerStyle相关的很多东西

* 实现了复制和粘贴
* 实现了LayerStyleContainer的<<运算符
* 重构了LayerContainerListWidget的信号结构
* 使LayerStyleContainer的一些接口的表现更符合新需求
* 让style继承的基本覆盖逻辑变得正确了
* Fix: LayerContainerListWidget的操作对象不及时改变
main
ArgonarioD 2023-03-24 00:47:55 +08:00
parent dc4de8823e
commit 7667525287
8 changed files with 151 additions and 74 deletions

View File

@ -1,7 +1,12 @@
#include "LayerContainerListWidget.h"
#include "LayerStyleDialog.h"
#include "../ColorHelper.hpp"
#include <QLabel>
#include <QApplication>
#include <QMessageBox>
#include <QClipboard>
#include <QJsonDocument>
#include <ranges>
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<LayerStyle>& style)
void LayerContainerListWidget::addLayerStyle(const std::shared_ptr<LayerStyle>& style)
{
if (const bool ok = styleContainer->useStyle(style))
{
@ -89,10 +89,18 @@ void LayerContainerListWidget::onAddLayerStyle(const std::shared_ptr<LayerStyle>
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<LayerStyle>& 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_ptr<LayerSty
if (dialog->layerStyle)
{
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);

View File

@ -26,13 +26,15 @@ public:
void setStyleContainer(const PLayerStyleContainer& styleContainer, bool forceRefresh = false);
PLayerStyleContainer getStyleContainer() const;
protected slots:
void onAddLayerStyle(const std::shared_ptr<LayerStyle>& style);
void onRemoveLayerStyle(const QString& styleName);
signals:
void addLayerStyle(const std::shared_ptr<LayerStyle>& style);
void editLayerStyle(const std::shared_ptr<LayerStyle>& style);
void removeLayerStyle(const QString& styleName);
protected slots:
void copyStyles() const;
void pasteStyles();
signals:
void refreshStylePreview();
};

View File

@ -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<Renderer::BaseStyle> LayerStyleContainer::toBaseStyles() const
@ -196,27 +196,32 @@ bool LayerStyleContainer::useStyle(const std::shared_ptr<LayerStyle>& 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<LayerStyle>& 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> StrokeElementLayerStyle::fromJson(const QJsonObject& json)
{
auto ptr = std::make_unique<StrokeElementLayerStyle>(
@ -351,6 +367,28 @@ std::unique_ptr<LayerStyle> StrokeElementLayerStyle::clone() const
return std::make_unique<StrokeElementLayerStyle>(StrokeElementLayerStyle(*this));
}
std::unique_ptr<LayerStyle> StrokeElementLayerStyle::fitForElementType(ElementType::ElementType type) const
{
auto result = std::unique_ptr<StrokeElementLayerStyle>(reinterpret_cast<StrokeElementLayerStyle*>(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<LayerStyle> FillElementLayerStyle::clone() const
return std::make_unique<FillElementLayerStyle>(FillElementLayerStyle(*this));
}
std::unique_ptr<LayerStyle> 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))

View File

@ -21,6 +21,14 @@ using Renderer::MaterialStyleFill;
#define radialStroke(stroke) std::static_pointer_cast<Renderer::StrokeRadialGradient>((stroke)->materialStroke)
#define plainFill(fill) std::static_pointer_cast<Renderer::FillPlain>((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<LayerStyle> clone() const = 0;
virtual std::unique_ptr<LayerStyle> 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<LayerStyle> clone() const override;
std::unique_ptr<LayerStyle> 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<LayerStyle> clone() const override;
std::unique_ptr<LayerStyle> fitForElementType(ElementType::ElementType type) const override;
bool operator==(const LayerStyle& other) const override;
@ -111,19 +122,15 @@ private:
[] { return std::make_unique<StrokeElementLayerStyle>(false); }
} };
ElementType::ElementType elementType;
DisplayNameWithSupplier unusedStyles;
DisplayNameWithSupplier usedStyles;
std::map<QString, std::shared_ptr<LayerStyle>> 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<Renderer::BaseStyle> 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);
/**
*

View File

@ -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<GroupElement *>(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)

View File

@ -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<LayerWrapper*>& 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<LayerWrapper*>& reachable) override;
QTreeWidgetItem* getQTreeItem() override;
void refreshTreeItem() override;

View File

@ -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<LayerStyle>& style) {
emit triggerCentralRefresh();
});
connect(ui.styleList, &LayerContainerListWidget::editLayerStyle, [this](const std::shared_ptr<LayerStyle>& 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<LayerWrapper*>(this->displayLayer)->styles);
ui.styleList->setStyleContainer(this->displayLayer->styles);
}
else
{
ui.styleList->setDisabled(true);
}
}*/
ui.styleList->setStyleContainer(this->displayLayer->styles);
}
}

View File

@ -16,8 +16,8 @@ namespace UnitTest
public:
LayerStyleContainerTest() :
argv{ const_cast<char*>("") }, 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());