karlis 2023-03-20 20:16:30 +08:00
commit 0543a02dfb
4 changed files with 102 additions and 70 deletions

View File

@ -3,6 +3,9 @@
#include "../ColorHelper.hpp" #include "../ColorHelper.hpp"
#include <qtmaterialraisedbutton.h> #include <qtmaterialraisedbutton.h>
#include <limits> #include <limits>
#include <array>
#include <ranges>
#include <utility>
constexpr int COLUMN_WIDTH = 0; constexpr int COLUMN_WIDTH = 0;
constexpr int COLUMN_COLOR = 1; constexpr int COLUMN_COLOR = 1;
@ -10,11 +13,27 @@ constexpr int COLUMN_METALLIC = 2;
constexpr int COLUMN_ROUGHNESS = 3; constexpr int COLUMN_ROUGHNESS = 3;
constexpr int COLUMN_OPERATIONS = 4; constexpr int COLUMN_OPERATIONS = 4;
inline Renderer::Material newMaterial()
{
return {ColorHelper::instance().getPrimary1()};
}
inline bool isClosedStroke(const std::shared_ptr<MaterialStyleStroke>& stroke)
{
return stroke->endType == Renderer::StrokeEndType::kClosed;
}
StrokeStyleWidget::StrokeStyleWidget( StrokeStyleWidget::StrokeStyleWidget(
std::shared_ptr<MaterialStyleStroke> stroke, const std::shared_ptr<MaterialStyleStroke>& stroke,
QWidget* parent QWidget* parent
) : QWidget(parent), stroke(stroke) ) : QWidget(parent), stroke(stroke)
{ {
auto& materialMap = radialStroke(stroke)->materialMap;
if (materialMap.empty())
{
materialMap[1.f] = newMaterial();
}
auto* viewLayout = new QVBoxLayout(this); auto* viewLayout = new QVBoxLayout(this);
this->setLayout(viewLayout); this->setLayout(viewLayout);
@ -25,13 +44,20 @@ StrokeStyleWidget::StrokeStyleWidget(
strokeProperties->setLayout(strokePropertiesLayout); strokeProperties->setLayout(strokePropertiesLayout);
strokePropertiesLayout->addWidget(enableGradual); strokePropertiesLayout->addWidget(enableGradual);
strokePropertiesLayout->addWidget(endTypeBox); if (!isClosedStroke(stroke))
{
strokePropertiesLayout->addWidget(endTypeBox);
}
viewLayout->addWidget(strokeProperties); viewLayout->addWidget(strokeProperties);
viewLayout->addWidget(widthField); viewLayout->addWidget(widthField);
initTable(std::dynamic_pointer_cast<Renderer::StrokeRadialGradient>(stroke->materialStroke)); initTable(radialStroke(stroke));
viewLayout->addWidget(strokeTable); viewLayout->addWidget(strokeTable);
initAddButton();
viewLayout->addWidget(addButton);
this->adjustSize(); this->adjustSize();
} }
@ -44,30 +70,23 @@ void StrokeStyleWidget::initStrokeSettings()
radialStroke(this->stroke)->gradual = checked; radialStroke(this->stroke)->gradual = checked;
}); });
#define endTypeBoxLabel(start, end) QStringLiteral(start##" -> "##end) if (!isClosedStroke(stroke))
this->endTypeBox = new QComboBox(this); {
endTypeBox->addItem(endTypeBoxLabel("圆头", "圆头")); // kRound this->endTypeBox = new QComboBox(this);
endTypeBox->addItem(endTypeBoxLabel("平头", "圆头")); // kFlatRound for (const auto& displayName : MaterialStyleStroke::strokeEndTypeNames | std::views::keys)
endTypeBox->addItem(endTypeBoxLabel("圆头", "平头")); // kRoundFlat
endTypeBox->addItem(endTypeBoxLabel("平头", "平头")); // kFlat
endTypeBox->setCurrentIndex(static_cast<int>(this->stroke->endType));
connect(endTypeBox, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index) {
switch (index)
{ {
case 0: endTypeBox->addItem(displayName);
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;
} }
}); endTypeBox->setCurrentIndex(static_cast<int>(this->stroke->endType));
connect(endTypeBox, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index) {
const auto& [displayName, endType] = MaterialStyleStroke::strokeEndTypeNames[index];
this->stroke->endType = endType;
});
}
else
{
this->endTypeBox = nullptr;
}
this->widthField = new QtMaterialTextField(this); this->widthField = new QtMaterialTextField(this);
widthField->setLabel(QStringLiteral("本侧描边宽度")); widthField->setLabel(QStringLiteral("本侧描边宽度"));
@ -83,13 +102,12 @@ void StrokeStyleWidget::initStrokeSettings()
}); });
} }
// TODO: 新增时参数校验 void StrokeStyleWidget::initTable(const std::shared_ptr<Renderer::StrokeRadialGradient>& materialStroke)
void StrokeStyleWidget::initTable(std::shared_ptr<Renderer::StrokeRadialGradient> materialStroke)
{ {
this->strokeTable = new QTableWidget(this); this->strokeTable = new QTableWidget(this);
strokeTable->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContentsOnFirstShow); strokeTable->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContentsOnFirstShow);
strokeTable->setColumnCount(5); strokeTable->setColumnCount(5);
strokeTable->setRowCount(materialStroke->materialMap.size() + 1); strokeTable->setRowCount(materialStroke->materialMap.size());
QStringList headers; QStringList headers;
headers << QStringLiteral("离心距离占比") headers << QStringLiteral("离心距离占比")
<< QStringLiteral("颜色") << QStringLiteral("颜色")
@ -97,44 +115,45 @@ void StrokeStyleWidget::initTable(std::shared_ptr<Renderer::StrokeRadialGradient
<< QStringLiteral("粗糙度") << QStringLiteral("粗糙度")
<< QStringLiteral("其他操作"); << QStringLiteral("其他操作");
strokeTable->setHorizontalHeaderLabels(headers); strokeTable->setHorizontalHeaderLabels(headers);
strokeTable->setMinimumHeight(strokeTable->rowHeight(0) * 5);
strokeTable->setMinimumWidth(strokeTable->sizeHint().width());
int row = 0; int row = 0;
// 内容 // 内容
for (auto & strokePair : materialStroke->materialMap) for (auto& [width, material] : std::views::reverse(materialStroke->materialMap))
{ {
setTableRow(row, strokePair.first, strokePair.second); setTableRow(row, width, material);
row++; row++;
} }
// 新增按钮 connect(strokeTable, &QTableWidget::currentItemChanged, this, &StrokeStyleWidget::onCurrentItemChanged);
auto* addButton = new QtMaterialRaisedButton("+", strokeTable); connect(strokeTable, &QTableWidget::cellChanged, this, &StrokeStyleWidget::onCellChanged);
}
void StrokeStyleWidget::initAddButton()
{
this->addButton = new QtMaterialRaisedButton("+", strokeTable);
addButton->setFixedHeight(this->strokeTable->rowHeight(0));
addButton->setBackgroundColor(ColorHelper::instance().getPrimary1()); addButton->setBackgroundColor(ColorHelper::instance().getPrimary1());
strokeTable->setSpan(row, 0, 1, 5); connect(addButton, &QtMaterialRaisedButton::clicked, [this] {
strokeTable->setCellWidget(row, 0, addButton);
strokeTable->setMinimumHeight(strokeTable->rowHeight(row) * 5);
strokeTable->setMinimumWidth(strokeTable->sizeHint().width());
addButton->setFixedHeight(strokeTable->rowHeight(row));
connect(addButton, &QtMaterialRaisedButton::clicked, [this]() {
handlingRowInsert = true; handlingRowInsert = true;
auto materialMap = &radialStroke(this->stroke)->materialMap; auto& materialMap = radialStroke(this->stroke)->materialMap;
float newWidth; float newWidth;
if (materialMap->empty()) if (materialMap.empty())
{ {
newWidth = 0.1; newWidth = 1.f;
} }
else else
{ {
const auto lastPair = materialMap->rbegin(); const auto firstPair = materialMap.begin();
newWidth = lastPair->first + 0.01; newWidth = firstPair->first / 2;
} }
const Renderer::Material newMaterial(ColorHelper::instance().getPrimary1()); const Renderer::Material newMaterial(ColorHelper::instance().getPrimary1());
(*materialMap)[newWidth] = newMaterial; materialMap[newWidth] = newMaterial;
int newRow = this->strokeTable->rowCount() - 1; const int newRow = this->strokeTable->rowCount();
this->strokeTable->insertRow(newRow); this->strokeTable->insertRow(newRow);
setTableRow(newRow, newWidth, (*materialMap)[newWidth]); setTableRow(newRow, newWidth, materialMap[newWidth]);
this->strokeTable->update(); this->strokeTable->update();
handlingRowInsert = false; 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) void StrokeStyleWidget::setTableRow(int row, float width, Renderer::Material& material)
@ -150,7 +169,7 @@ void StrokeStyleWidget::setTableRow(int row, float width, Renderer::Material& ma
auto* colorPicker = new ColorPicker(*colorPtr, strokeTable); auto* colorPicker = new ColorPicker(*colorPtr, strokeTable);
strokeTable->setCellWidget(row, COLUMN_COLOR, colorPicker); strokeTable->setCellWidget(row, COLUMN_COLOR, colorPicker);
connect(colorPicker, &ColorPicker::colorChanged, [this, colorPtr](QColor color) { connect(colorPicker, &ColorPicker::colorChanged, [this, colorPtr](QColor color) {
*colorPtr = color; *colorPtr = std::move(color);
this->strokeTable->update(); this->strokeTable->update();
}); });
@ -166,7 +185,7 @@ void StrokeStyleWidget::setTableRow(int row, float width, Renderer::Material& ma
removeButton->setBackgroundColor(ColorHelper::instance().getPrimary1()); removeButton->setBackgroundColor(ColorHelper::instance().getPrimary1());
removeButton->setFixedSize(20, 20); removeButton->setFixedSize(20, 20);
strokeTable->setCellWidget(row, COLUMN_OPERATIONS, removeButton); strokeTable->setCellWidget(row, COLUMN_OPERATIONS, removeButton);
connect(removeButton, &QtMaterialRaisedButton::clicked, [this, row]() { connect(removeButton, &QtMaterialRaisedButton::clicked, [this, row] {
radialStroke(this->stroke)->materialMap.erase(this->strokeTable->item(row, COLUMN_WIDTH)->text().toFloat()); radialStroke(this->stroke)->materialMap.erase(this->strokeTable->item(row, COLUMN_WIDTH)->text().toFloat());
this->strokeTable->removeRow(row); this->strokeTable->removeRow(row);
}); });
@ -175,7 +194,7 @@ void StrokeStyleWidget::setTableRow(int row, float width, Renderer::Material& ma
void StrokeStyleWidget::onCurrentItemChanged(QTableWidgetItem* current, QTableWidgetItem* previous) void StrokeStyleWidget::onCurrentItemChanged(QTableWidgetItem* current, QTableWidgetItem* previous)
{ {
if (!current) return; if (!current) return;
int column = current->column(); const int column = current->column();
if (column != COLUMN_COLOR && column != COLUMN_OPERATIONS) if (column != COLUMN_COLOR && column != COLUMN_OPERATIONS)
{ {
this->currentItemValue = current->data(Qt::EditRole); this->currentItemValue = current->data(Qt::EditRole);
@ -185,23 +204,27 @@ void StrokeStyleWidget::onCurrentItemChanged(QTableWidgetItem* current, QTableWi
void StrokeStyleWidget::onCellChanged(int row, int column) void StrokeStyleWidget::onCellChanged(int row, int column)
{ {
if (handlingRowInsert) return; if (handlingRowInsert) return;
auto changedItem = strokeTable->item(row, column); const auto changedItem = strokeTable->item(row, column);
auto changedItemValue = changedItem->text().toFloat(); const auto changedItemValue = changedItem->text().toFloat();
if (changedItemValue < 0 || 1 < changedItemValue) if (changedItemValue < 0 || 1 < changedItemValue)
{ {
changedItem->setData(Qt::EditRole, this->currentItemValue.toFloat()); changedItem->setData(Qt::EditRole, this->currentItemValue.toFloat());
return; return;
} }
auto changedWidth = strokeTable->item(row, COLUMN_WIDTH)->data(Qt::EditRole).toFloat(); const auto changedWidth = strokeTable->item(row, COLUMN_WIDTH)->data(Qt::EditRole).toFloat();
switch (column) switch (column)
{ {
case COLUMN_WIDTH: case COLUMN_WIDTH:
{ {
float oldWidth = this->currentItemValue.toFloat(); float oldWidth = this->currentItemValue.toFloat();
auto node = radialStroke(stroke)->materialMap.extract(oldWidth); auto node = radialStroke(stroke)->materialMap.extract(oldWidth);
if (node.empty())
{
break;
}
node.key() = changedWidth; node.key() = changedWidth;
radialStroke(stroke)->materialMap.insert(std::move(node)); radialStroke(stroke)->materialMap.insert(std::move(node));
strokeTable->sortItems(COLUMN_WIDTH); strokeTable->sortItems(COLUMN_WIDTH, Qt::DescendingOrder);
break; break;
} }
case COLUMN_METALLIC: case COLUMN_METALLIC:

View File

@ -6,6 +6,7 @@
#include <QComboBox> #include <QComboBox>
#include <qtmaterialtextfield.h> #include <qtmaterialtextfield.h>
#include <qtmaterialcheckbox.h> #include <qtmaterialcheckbox.h>
#include <qtmaterialraisedbutton.h>
class StrokeStyleWidget : public QWidget class StrokeStyleWidget : public QWidget
{ {
Q_OBJECT Q_OBJECT
@ -16,14 +17,16 @@ private:
QComboBox* endTypeBox; QComboBox* endTypeBox;
QtMaterialTextField* widthField; QtMaterialTextField* widthField;
QTableWidget* strokeTable; QTableWidget* strokeTable;
QtMaterialRaisedButton* addButton;
bool handlingRowInsert = false; bool handlingRowInsert = false;
void initStrokeSettings(); void initStrokeSettings();
void initTable(std::shared_ptr<Renderer::StrokeRadialGradient> materialStroke); void initTable(const std::shared_ptr<Renderer::StrokeRadialGradient>& materialStroke);
void initAddButton();
void setTableRow(int row, float width, Renderer::Material& material); void setTableRow(int row, float width, Renderer::Material& material);
public: public:
StrokeStyleWidget(std::shared_ptr<MaterialStyleStroke> stroke, QWidget* parent = nullptr); StrokeStyleWidget(const std::shared_ptr<MaterialStyleStroke>& stroke, QWidget* parent = nullptr);
std::shared_ptr<MaterialStyleStroke> stroke; std::shared_ptr<MaterialStyleStroke> stroke;
protected slots: protected slots:

View File

@ -248,18 +248,18 @@ std::unique_ptr<StrokeElementLayerStyle> StrokeElementLayerStyle::fromJson(const
return ptr; return ptr;
} }
StrokeElementLayerStyle::StrokeElementLayerStyle() StrokeElementLayerStyle::StrokeElementLayerStyle(bool isClosed)
{ {
const auto materialMap = std::map<float, Renderer::Material>(); const auto materialMap = std::map<float, Renderer::Material>();
this->strokePair.first = std::make_shared<MaterialStyleStroke>( this->strokePair.first = std::make_shared<MaterialStyleStroke>(
7, 7,
Renderer::StrokeType::kLeftSide, Renderer::StrokeEndType::kFlat, Renderer::StrokeType::kLeftSide, isClosed ? Renderer::StrokeEndType::kClosed : Renderer::StrokeEndType::kFlat,
std::make_shared<Renderer::StrokeRadialGradient>(materialMap, false) std::make_shared<Renderer::StrokeRadialGradient>(materialMap, false)
); );
this->strokePair.second = std::make_shared<MaterialStyleStroke>( this->strokePair.second = std::make_shared<MaterialStyleStroke>(
7, 7,
Renderer::StrokeType::kRightSide, Renderer::StrokeEndType::kFlat, Renderer::StrokeType::kRightSide, isClosed ? Renderer::StrokeEndType::kClosed : Renderer::StrokeEndType::kFlat,
std::make_shared<Renderer::StrokeRadialGradient>(materialMap, false) std::make_shared<Renderer::StrokeRadialGradient>(materialMap, false)
); );

View File

@ -46,7 +46,7 @@ public:
STYLE_NAME("Ãè±ß", "stroke") STYLE_NAME("Ãè±ß", "stroke")
static std::unique_ptr<StrokeElementLayerStyle> fromJson(const QJsonObject& json); static std::unique_ptr<StrokeElementLayerStyle> fromJson(const QJsonObject& json);
StrokeElementLayerStyle(); StrokeElementLayerStyle(bool isClosed);
StrokeElementLayerStyle(const PMaterialStyleStroke& left, const PMaterialStyleStroke& right = nullptr); StrokeElementLayerStyle(const PMaterialStyleStroke& left, const PMaterialStyleStroke& right = nullptr);
StrokeElementLayerStyle(const StrokeElementLayerStyle& other); StrokeElementLayerStyle(const StrokeElementLayerStyle& other);
~StrokeElementLayerStyle() override = default; ~StrokeElementLayerStyle() override = default;
@ -88,17 +88,23 @@ public:
class LayerStyleContainer : public Renderer::ElementStyle class LayerStyleContainer : public Renderer::ElementStyle
{ {
using DisplayNameWithSupplier = std::map<QString, std::function<std::unique_ptr<LayerStyle>()>>; using DisplayNameWithSupplier = std::map<QString, std::function<std::unique_ptr<LayerStyle>()>>;
private:
inline const static DisplayNameWithSupplier commonStyles = { { private:
inline const static DisplayNameWithSupplier commonStyles = { };
inline const static DisplayNameWithSupplier closedOnlyStyles = {
{
FillElementLayerStyle::displayName(),
[] { return std::make_unique<FillElementLayerStyle>(); }
},
{
StrokeElementLayerStyle::displayName(),
[] { return std::make_unique<StrokeElementLayerStyle>(true); }
}
};
inline const static DisplayNameWithSupplier unclosedOnlyStyles = { {
StrokeElementLayerStyle::displayName(), StrokeElementLayerStyle::displayName(),
[] { return std::make_unique<StrokeElementLayerStyle>(); } [] { return std::make_unique<StrokeElementLayerStyle>(false); }
} }; } };
inline const static DisplayNameWithSupplier closedOnlyStyles = { {
FillElementLayerStyle::displayName(),
[] { return std::make_unique<FillElementLayerStyle>(); }
} };
inline const static DisplayNameWithSupplier unclosedOnlyStyles = { };
DisplayNameWithSupplier unusedStyles; DisplayNameWithSupplier unusedStyles;
DisplayNameWithSupplier usedStyles; DisplayNameWithSupplier usedStyles;