Compare commits
No commits in common. "72f0f78e644a783aa8096a479a25804c8d37e936" and "de9d7143b6260f606cfe37f6cb1a9a2cb8d50731" have entirely different histories.
72f0f78e64
...
de9d7143b6
|
@ -6,8 +6,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>1473</width>
|
<width>1124</width>
|
||||||
<height>1103</height>
|
<height>1010</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
|
@ -56,20 +56,7 @@
|
||||||
<widget class="QWidget" name="LeftBar" native="true"/>
|
<widget class="QWidget" name="LeftBar" native="true"/>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="PreviewWindow" name="Preview">
|
<widget class="PreviewWindow" name="Preview"/>
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>1080</width>
|
|
||||||
<height>1080</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>1080</width>
|
|
||||||
<height>1080</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QWidget" name="RightBar" native="true">
|
<widget class="QWidget" name="RightBar" native="true">
|
||||||
|
|
|
@ -59,11 +59,7 @@ EditorWidgetItem::~EditorWidgetItem()
|
||||||
|
|
||||||
void EditorWidgetItem::paintEvent(QPaintEvent *event)
|
void EditorWidgetItem::paintEvent(QPaintEvent *event)
|
||||||
{
|
{
|
||||||
QPainter painter(this);
|
|
||||||
|
|
||||||
// 设置画刷的颜色为灰色,并填充整个窗口区域
|
|
||||||
painter.setBrush(Qt::gray);
|
|
||||||
painter.drawRect(this->rect());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorWidgetItem::onLayerChange(LayerWrapper *layer)
|
void EditorWidgetItem::onLayerChange(LayerWrapper *layer)
|
||||||
|
|
|
@ -84,18 +84,3 @@ QJsonObject GraphicElement::toJson() const
|
||||||
result.insert("name", name);
|
result.insert("name", name);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SimpleElement::paint(QPainter* painter, QTransform transform, vector<std::shared_ptr<LayerStyle>> styles)
|
|
||||||
{
|
|
||||||
Renderer::ElementStyleStrokeDemo demo(2);
|
|
||||||
auto [img, mov] = renderer->drawElement(painterPath, demo, 1.0);
|
|
||||||
painter->save();
|
|
||||||
painter->setTransform(transform);
|
|
||||||
painter->drawImage(mov, img);
|
|
||||||
painter->restore();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GroupElement::paint(QPainter* painter, QTransform transform, vector<std::shared_ptr<LayerStyle>> styles)
|
|
||||||
{
|
|
||||||
sourceLayer->paint(painter, transform);
|
|
||||||
}
|
|
|
@ -28,7 +28,6 @@ public:
|
||||||
virtual QJsonObject toJson() const;
|
virtual QJsonObject toJson() const;
|
||||||
virtual PixelPath getPaintObject() const = 0;
|
virtual PixelPath getPaintObject() const = 0;
|
||||||
virtual PixelPath getPaintObject(std::vector<std::shared_ptr<LayerStyle>>*) const = 0;
|
virtual PixelPath getPaintObject(std::vector<std::shared_ptr<LayerStyle>>*) const = 0;
|
||||||
virtual void paint(QPainter* painter, QTransform transform, std::vector<std::shared_ptr<LayerStyle>> styles) = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class SimpleElement : public GraphicElement
|
class SimpleElement : public GraphicElement
|
||||||
|
@ -45,7 +44,6 @@ public:
|
||||||
~SimpleElement() = default;
|
~SimpleElement() = default;
|
||||||
PixelPath getPaintObject() const override;
|
PixelPath getPaintObject() const override;
|
||||||
PixelPath getPaintObject(std::vector<std::shared_ptr<LayerStyle>>*) const override;
|
PixelPath getPaintObject(std::vector<std::shared_ptr<LayerStyle>>*) const override;
|
||||||
void paint(QPainter* painter, QTransform transform, std::vector<std::shared_ptr<LayerStyle>> styles) override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class GroupElement : public GraphicElement
|
class GroupElement : public GraphicElement
|
||||||
|
@ -60,7 +58,6 @@ public:
|
||||||
PixelPath getPaintObject() const override;
|
PixelPath getPaintObject() const override;
|
||||||
PixelPath getPaintObject(std::vector<std::shared_ptr<LayerStyle>>*) const override;
|
PixelPath getPaintObject(std::vector<std::shared_ptr<LayerStyle>>*) const override;
|
||||||
void setSourceLayer(FolderLayerWrapper* sourceLayer);
|
void setSourceLayer(FolderLayerWrapper* sourceLayer);
|
||||||
void paint(QPainter* painter, QTransform transform, std::vector<std::shared_ptr<LayerStyle>> styles) override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//******************************** BitmapPath ********************************//
|
//******************************** BitmapPath ********************************//
|
||||||
|
|
|
@ -11,10 +11,10 @@ LayerWrapper *LayerManager::getRoot() const
|
||||||
{
|
{
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
void LayerManager::paint(QPainter *painter, QSize size,LayerWrapper* selecetedLayer) const
|
void LayerManager::paint(QPainter *painter, QSize size) const
|
||||||
{
|
{
|
||||||
root->getCache();
|
auto p = root->getCache().resizedPixel(size);
|
||||||
root->paint(painter);
|
painter->drawPixmap(0, 0, p);
|
||||||
}
|
}
|
||||||
bool LayerManager::singleSelectedCheck() const
|
bool LayerManager::singleSelectedCheck() const
|
||||||
{
|
{
|
||||||
|
|
|
@ -32,7 +32,7 @@ class LayerManager
|
||||||
LayerManager() = default;
|
LayerManager() = default;
|
||||||
LayerManager(QJsonObject source, ElementManager* elementManager);
|
LayerManager(QJsonObject source, ElementManager* elementManager);
|
||||||
QJsonObject toJson() const;
|
QJsonObject toJson() const;
|
||||||
void paint(QPainter *painter, QSize size, LayerWrapper* selecetedLayer=nullptr) const;
|
void paint(QPainter *painter, QSize size) const;
|
||||||
bool rename(QString newName) const;
|
bool rename(QString newName) const;
|
||||||
bool combine() const;
|
bool combine() const;
|
||||||
// bool seperate() const;
|
// bool seperate() const;
|
||||||
|
|
|
@ -23,13 +23,9 @@ FolderLayerWrapper*LayerWrapper::getParent() const
|
||||||
return this == nullptr ? nullptr : this->parent;
|
return this == nullptr ? nullptr : this->parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
PixelPath LayerWrapper::getCache(LayerWrapper* selectedLayer)
|
PixelPath LayerWrapper::getCache()
|
||||||
{
|
{
|
||||||
this->refresh(selectedLayer);
|
this->refresh();
|
||||||
if (selectedLayer == this)
|
|
||||||
{
|
|
||||||
this->cache.highLight();
|
|
||||||
}
|
|
||||||
return cache;
|
return cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +42,6 @@ LayerWrapper::LayerWrapper(QJsonObject json, FolderLayerWrapper*parent, ElementM
|
||||||
property.scale = {transformJson.value("scale").toObject().value("x").toDouble(),
|
property.scale = {transformJson.value("scale").toObject().value("x").toDouble(),
|
||||||
transformJson.value("scale").toObject().value("y").toDouble()};
|
transformJson.value("scale").toObject().value("y").toDouble()};
|
||||||
property.rotation = {transformJson.value("rotation").toDouble()};
|
property.rotation = {transformJson.value("rotation").toDouble()};
|
||||||
selected = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FolderLayerWrapper::FolderLayerWrapper(QJsonObject json, ElementManager *elementManager, FolderLayerWrapper*parent)
|
FolderLayerWrapper::FolderLayerWrapper(QJsonObject json, ElementManager *elementManager, FolderLayerWrapper*parent)
|
||||||
|
@ -80,52 +75,35 @@ LeafLayerWrapper::LeafLayerWrapper(QJsonObject json, ElementManager *elementMana
|
||||||
int elementIndex = json.value("element").toInt();
|
int elementIndex = json.value("element").toInt();
|
||||||
wrappedElement = elementManager->getElementById(elementIndex);
|
wrappedElement = elementManager->getElementById(elementIndex);
|
||||||
}
|
}
|
||||||
|
void LayerWrapper::SimpleProperty::apply(PixelPath&cache) const
|
||||||
void LayerWrapper::SimpleProperty::apply(PixelPath&cache)
|
|
||||||
{
|
|
||||||
transform.reset();
|
|
||||||
double centerX = cache.getBoundingRect().center().x();
|
|
||||||
double centerY = cache.getBoundingRect().center().y();
|
|
||||||
//qDebug() << name << " " << cache.boundingRect().center();
|
|
||||||
//qDebug() << name << " " << cache.boundingRect();
|
|
||||||
transform.translate(-centerX, -centerY);
|
|
||||||
transform.translate(offset.x(), offset.y());
|
|
||||||
transform.rotate(rotation);
|
|
||||||
transform.scale(scale.x(), scale.y());
|
|
||||||
transform.translate(centerX, centerY);
|
|
||||||
cache = cache.trans(transform);
|
|
||||||
}
|
|
||||||
|
|
||||||
QTransform LayerWrapper::getTransform()
|
|
||||||
{
|
{
|
||||||
QTransform trans;
|
QTransform trans;
|
||||||
double centerX = cache.getBoundingRect().center().x();
|
double centerX = cache.getBoundingRect().center().x();
|
||||||
double centerY = cache.getBoundingRect().center().y();
|
double centerY = cache.getBoundingRect().center().y();
|
||||||
//qDebug() << name << " " << cache.boundingRect().center();
|
//qDebug() << name << " " << cache.boundingRect().center();
|
||||||
//qDebug() << name << " " << cache.boundingRect();
|
//qDebug() << name << " " << cache.boundingRect();
|
||||||
trans.translate(centerX, centerY);
|
trans.translate(offset.x(), offset.y());
|
||||||
trans.translate(property.offset.x(), property.offset.y());
|
|
||||||
trans.rotate(property.rotation);
|
|
||||||
trans.scale(property.scale.x(), property.scale.y());
|
|
||||||
trans.translate(-centerX, -centerY);
|
trans.translate(-centerX, -centerY);
|
||||||
return trans;
|
trans.rotate(rotation);
|
||||||
|
trans.scale(scale.x(), scale.y());
|
||||||
|
trans.translate(centerX, centerY);
|
||||||
|
cache = cache.trans(trans);
|
||||||
}
|
}
|
||||||
|
void LayerWrapper::refresh()
|
||||||
void LayerWrapper::refresh(LayerWrapper* layer)
|
|
||||||
{
|
{
|
||||||
property.apply(cache);
|
property.apply(cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FolderLayerWrapper::refresh(LayerWrapper* layer)
|
void FolderLayerWrapper::refresh()
|
||||||
{
|
{
|
||||||
cache.clear();
|
cache.clear();
|
||||||
for (auto& child : children) {
|
for (auto& child : children) {
|
||||||
cache.addPath(child.get()->getCache(layer));
|
cache.addPath(child.get()->getCache());
|
||||||
}
|
}
|
||||||
LayerWrapper::refresh();
|
LayerWrapper::refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LeafLayerWrapper::refresh(LayerWrapper* layer)
|
void LeafLayerWrapper::refresh()
|
||||||
{
|
{
|
||||||
cache.clear();
|
cache.clear();
|
||||||
if (wrappedElement != nullptr)
|
if (wrappedElement != nullptr)
|
||||||
|
@ -250,36 +228,3 @@ int FolderLayerWrapper::getReferencedBy()const
|
||||||
else
|
else
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LayerWrapper::paint(QPainter* painter, QTransform transform)
|
|
||||||
{
|
|
||||||
if (this->selected)
|
|
||||||
{
|
|
||||||
painter->save();
|
|
||||||
painter->setTransform(transform);
|
|
||||||
painter->setPen(QPen(Qt::gray, 2));
|
|
||||||
painter->setPen(Qt::DashLine);
|
|
||||||
painter->drawRect(cache.getBoundingRect());
|
|
||||||
painter->restore();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderLayerWrapper::paint(QPainter* painter, QTransform transform)
|
|
||||||
{
|
|
||||||
LayerWrapper::paint(painter, transform);
|
|
||||||
transform = property.transform * transform;
|
|
||||||
qDebug() << transform;
|
|
||||||
for (auto& child : children)
|
|
||||||
child->paint(painter, transform);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LeafLayerWrapper::paint(QPainter* painter, QTransform transform)
|
|
||||||
{
|
|
||||||
LayerWrapper::paint(painter, transform);
|
|
||||||
transform = property.transform * transform;
|
|
||||||
qDebug() << transform;
|
|
||||||
if (wrappedElement != nullptr)
|
|
||||||
{
|
|
||||||
wrappedElement->paint(painter, transform, styles);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -35,7 +35,6 @@ class LayerWrapper
|
||||||
|
|
||||||
public:
|
public:
|
||||||
QTreeWidgetItem* qTreeWidgetItem;
|
QTreeWidgetItem* qTreeWidgetItem;
|
||||||
bool selected;
|
|
||||||
struct SimpleProperty
|
struct SimpleProperty
|
||||||
{
|
{
|
||||||
QString name = "";
|
QString name = "";
|
||||||
|
@ -44,20 +43,17 @@ class LayerWrapper
|
||||||
double rotation = 0;
|
double rotation = 0;
|
||||||
bool flipHorizontally = 0;
|
bool flipHorizontally = 0;
|
||||||
bool flipVertically = 0;
|
bool flipVertically = 0;
|
||||||
QTransform transform;
|
|
||||||
// TODO: 将QPainterPath改为BitmapPath
|
// TODO: 将QPainterPath改为BitmapPath
|
||||||
void apply(PixelPath&cache);
|
void apply(PixelPath&cache) const;
|
||||||
} property;
|
} property;
|
||||||
virtual void setParent(FolderLayerWrapper*newParent);
|
virtual void setParent(FolderLayerWrapper*newParent);
|
||||||
virtual void refresh(LayerWrapper* layer = nullptr);
|
virtual void refresh();
|
||||||
virtual QTreeWidgetItem* getQTreeItem();
|
virtual QTreeWidgetItem* getQTreeItem();
|
||||||
// TODO: 将QPainterPath改为BitmapPath/QImage,或者直接将其删除,绘制时直接使用BitmapPath的paint方法
|
// TODO: 将QPainterPath改为BitmapPath/QImage,或者直接将其删除,绘制时直接使用BitmapPath的paint方法
|
||||||
virtual PixelPath getCache(LayerWrapper* selectedLayer=nullptr);
|
virtual PixelPath getCache();
|
||||||
QTransform getTransform();
|
|
||||||
FolderLayerWrapper*getParent() const; // invoke by manager, then invoke parent's applyStyles
|
FolderLayerWrapper*getParent() const; // invoke by manager, then invoke parent's applyStyles
|
||||||
LayerWrapper(QJsonObject json, FolderLayerWrapper*parent, ElementManager* elementManager=nullptr);
|
LayerWrapper(QJsonObject json, FolderLayerWrapper*parent, ElementManager* elementManager=nullptr);
|
||||||
LayerWrapper() = default;
|
LayerWrapper() = default;
|
||||||
virtual void paint(QPainter* painter, QTransform transform=QTransform());
|
|
||||||
// TODO : export Function
|
// TODO : export Function
|
||||||
// virtual LayerWrapper *addChild() = 0; // Leaf Child Only
|
// virtual LayerWrapper *addChild() = 0; // Leaf Child Only
|
||||||
// virtual LayerWrapper *addParent() = 0; // Folder Parent Only
|
// virtual LayerWrapper *addParent() = 0; // Folder Parent Only
|
||||||
|
@ -79,7 +75,7 @@ class FolderLayerWrapper : public LayerWrapper
|
||||||
public:
|
public:
|
||||||
|
|
||||||
~FolderLayerWrapper() = default;
|
~FolderLayerWrapper() = default;
|
||||||
void refresh(LayerWrapper* layer=nullptr) override;
|
void refresh() override;
|
||||||
FolderLayerWrapper() = default;
|
FolderLayerWrapper() = default;
|
||||||
FolderLayerWrapper(QJsonObject json, ElementManager *elementManager, FolderLayerWrapper*parent);
|
FolderLayerWrapper(QJsonObject json, ElementManager *elementManager, FolderLayerWrapper*parent);
|
||||||
void addChild(shared_ptr<LayerWrapper> child);
|
void addChild(shared_ptr<LayerWrapper> child);
|
||||||
|
@ -90,7 +86,6 @@ class FolderLayerWrapper : public LayerWrapper
|
||||||
QTreeWidgetItem* getQTreeItem() override;
|
QTreeWidgetItem* getQTreeItem() override;
|
||||||
QJsonObject toJson() const override;
|
QJsonObject toJson() const override;
|
||||||
int getReferencedBy()const;
|
int getReferencedBy()const;
|
||||||
void paint(QPainter* painter, QTransform transform = QTransform()) override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class LeafLayerWrapper : public LayerWrapper
|
class LeafLayerWrapper : public LayerWrapper
|
||||||
|
@ -102,11 +97,10 @@ class LeafLayerWrapper : public LayerWrapper
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~LeafLayerWrapper() = default;
|
~LeafLayerWrapper() = default;
|
||||||
void refresh(LayerWrapper* layer = nullptr) override;
|
void refresh() override;
|
||||||
LeafLayerWrapper() = default;
|
LeafLayerWrapper() = default;
|
||||||
LeafLayerWrapper(QJsonObject json, ElementManager *elementManager, FolderLayerWrapper*parent);
|
LeafLayerWrapper(QJsonObject json, ElementManager *elementManager, FolderLayerWrapper*parent);
|
||||||
QJsonObject toJson() const override;
|
QJsonObject toJson() const override;
|
||||||
void paint(QPainter* painter, QTransform transform = QTransform()) override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(LayerWrapper *)
|
Q_DECLARE_METATYPE(LayerWrapper *)
|
||||||
|
|
|
@ -103,18 +103,3 @@ QPixmap PixelPath::getDetail()const
|
||||||
result = pixmap.copy(rect);
|
result = pixmap.copy(rect);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PixelPath::highLight()
|
|
||||||
{
|
|
||||||
// 创建一个QPainter对象,关联到QPixmap对象
|
|
||||||
QPainter painter(&pixmap);
|
|
||||||
|
|
||||||
// 设置画笔的颜色、宽度和样式
|
|
||||||
painter.setPen(QPen(Qt::black, 1, Qt::DashLine));
|
|
||||||
|
|
||||||
// 绘制一个矩形,指定左上角和右下角的坐标
|
|
||||||
painter.drawRect(boundingRect);
|
|
||||||
|
|
||||||
// 结束绘制
|
|
||||||
painter.end();
|
|
||||||
}
|
|
|
@ -26,5 +26,4 @@ public:
|
||||||
PixelPath trans(QTransform& mat)const;
|
PixelPath trans(QTransform& mat)const;
|
||||||
QPixmap resizedPixel(QSize size)const;
|
QPixmap resizedPixel(QSize size)const;
|
||||||
QPixmap getDetail()const;
|
QPixmap getDetail()const;
|
||||||
void highLight();
|
|
||||||
};
|
};
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
PreviewWindow::PreviewWindow(QWidget *parent) : QOpenGLWidget(parent)
|
PreviewWindow::PreviewWindow(QWidget *parent) : QOpenGLWidget(parent)
|
||||||
{
|
{
|
||||||
this->setFixedSize(QSize(1080, 1080));
|
|
||||||
this->renderer = Renderer::ElementRenderer::instance();
|
this->renderer = Renderer::ElementRenderer::instance();
|
||||||
QSurfaceFormat surfaceFormat;
|
QSurfaceFormat surfaceFormat;
|
||||||
surfaceFormat.setSamples(16);
|
surfaceFormat.setSamples(16);
|
||||||
|
@ -48,7 +47,15 @@ void PreviewWindow::paintGL()
|
||||||
painter->begin(this);
|
painter->begin(this);
|
||||||
painter->setRenderHint(QPainter::Antialiasing);
|
painter->setRenderHint(QPainter::Antialiasing);
|
||||||
painter->setRenderHint(QPainter::HighQualityAntialiasing);
|
painter->setRenderHint(QPainter::HighQualityAntialiasing);
|
||||||
layerManager->paint(painter,this->size(),currentLayer);
|
layerManager->paint(painter,this->size());
|
||||||
|
if (currentLayer != nullptr)
|
||||||
|
{
|
||||||
|
QPen pen = painter->pen(); // 获取当前的画笔
|
||||||
|
pen.setStyle(Qt::DashLine); // 设置画笔样式为虚线
|
||||||
|
painter->setPen(pen); // 应用画笔设置
|
||||||
|
painter->drawRect(this->currentLayer->getCache().getBoundingRect()); // 绘制矩形
|
||||||
|
}
|
||||||
|
|
||||||
painter->end();
|
painter->end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,13 +12,9 @@ LayerTreeWidget::LayerTreeWidget(QWidget *parent)
|
||||||
this->setHeaderLabel("Layer Content");
|
this->setHeaderLabel("Layer Content");
|
||||||
connect(this, &QTreeWidget::customContextMenuRequested, this, &LayerTreeWidget::popMenu);
|
connect(this, &QTreeWidget::customContextMenuRequested, this, &LayerTreeWidget::popMenu);
|
||||||
connect(this, &QTreeWidget::currentItemChanged, [=](QTreeWidgetItem *currentItem) {
|
connect(this, &QTreeWidget::currentItemChanged, [=](QTreeWidgetItem *currentItem) {
|
||||||
if (this->selectedItem != nullptr) {
|
|
||||||
this->selectedItem->data(0, Qt::UserRole).value<LayerWrapper*>()->selected = false;
|
|
||||||
}
|
|
||||||
this->selectedItem = currentItem;
|
this->selectedItem = currentItem;
|
||||||
if (this->selectedItem != nullptr) {
|
if (this->selectedItem != nullptr) {
|
||||||
auto layer = this->selectedItem->data(0, Qt::UserRole).value<LayerWrapper*>();
|
auto layer = this->selectedItem->data(0, Qt::UserRole).value<LayerWrapper*>();
|
||||||
layer->selected = true;
|
|
||||||
emit displayLayerChange(layer);
|
emit displayLayerChange(layer);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
Loading…
Reference in New Issue