2022-10-08 01:05:53 +00:00
|
|
|
#include "component_property_widget.h"
|
2022-10-08 03:46:57 +00:00
|
|
|
#include "document.h"
|
2022-10-18 09:35:04 +00:00
|
|
|
#include "float_number_widget.h"
|
2022-10-08 03:46:57 +00:00
|
|
|
#include "theme.h"
|
2022-10-18 09:35:04 +00:00
|
|
|
#include <QColorDialog>
|
|
|
|
#include <QGroupBox>
|
|
|
|
#include <QHBoxLayout>
|
|
|
|
#include <QPushButton>
|
|
|
|
#include <QVBoxLayout>
|
|
|
|
#include <unordered_set>
|
2022-10-08 01:05:53 +00:00
|
|
|
|
2022-10-18 09:35:04 +00:00
|
|
|
ComponentPropertyWidget::ComponentPropertyWidget(Document* document,
|
|
|
|
const std::vector<dust3d::Uuid>& componentIds,
|
|
|
|
QWidget* parent)
|
|
|
|
: QWidget(parent)
|
|
|
|
, m_document(document)
|
|
|
|
, m_componentIds(componentIds)
|
2022-10-08 01:05:53 +00:00
|
|
|
{
|
2022-10-08 03:46:57 +00:00
|
|
|
preparePartIds();
|
|
|
|
m_color = lastColor();
|
|
|
|
|
2022-10-18 09:35:04 +00:00
|
|
|
QPushButton* colorPreviewArea = new QPushButton;
|
2022-10-08 03:46:57 +00:00
|
|
|
colorPreviewArea->setStyleSheet("QPushButton {background-color: " + m_color.name() + "; border-radius: 0;}");
|
|
|
|
colorPreviewArea->setFixedSize(Theme::toolIconSize * 1.8, Theme::toolIconSize);
|
|
|
|
|
2022-10-18 09:35:04 +00:00
|
|
|
QPushButton* colorPickerButton = new QPushButton(QChar(fa::eyedropper));
|
2022-10-14 11:11:41 +00:00
|
|
|
Theme::initIconButton(colorPickerButton);
|
2022-10-08 03:46:57 +00:00
|
|
|
connect(colorPickerButton, &QPushButton::clicked, this, &ComponentPropertyWidget::showColorDialog);
|
|
|
|
|
2022-10-18 09:35:04 +00:00
|
|
|
QHBoxLayout* colorLayout = new QHBoxLayout;
|
2022-10-08 03:46:57 +00:00
|
|
|
colorLayout->addWidget(colorPreviewArea);
|
|
|
|
colorLayout->addWidget(colorPickerButton);
|
2022-10-14 11:11:41 +00:00
|
|
|
colorLayout->addStretch();
|
2022-10-08 03:46:57 +00:00
|
|
|
colorLayout->setSizeConstraint(QLayout::SetFixedSize);
|
|
|
|
|
2022-10-18 09:35:04 +00:00
|
|
|
QGroupBox* deformGroupBox = nullptr;
|
2022-10-14 11:11:41 +00:00
|
|
|
if (nullptr != m_part) {
|
2022-10-18 09:35:04 +00:00
|
|
|
FloatNumberWidget* thicknessWidget = new FloatNumberWidget;
|
2022-10-14 11:11:41 +00:00
|
|
|
thicknessWidget->setItemName(tr("Thickness"));
|
|
|
|
thicknessWidget->setRange(0, 2);
|
|
|
|
thicknessWidget->setValue(m_part->deformThickness);
|
2022-10-18 09:35:04 +00:00
|
|
|
|
2022-10-14 11:11:41 +00:00
|
|
|
connect(thicknessWidget, &FloatNumberWidget::valueChanged, [=](float value) {
|
|
|
|
emit setPartDeformThickness(m_partId, value);
|
|
|
|
emit groupOperationAdded();
|
|
|
|
});
|
2022-10-18 09:35:04 +00:00
|
|
|
|
|
|
|
FloatNumberWidget* widthWidget = new FloatNumberWidget;
|
2022-10-14 11:11:41 +00:00
|
|
|
widthWidget->setItemName(tr("Width"));
|
|
|
|
widthWidget->setRange(0, 2);
|
|
|
|
widthWidget->setValue(m_part->deformWidth);
|
2022-10-18 09:35:04 +00:00
|
|
|
|
2022-10-14 11:11:41 +00:00
|
|
|
connect(widthWidget, &FloatNumberWidget::valueChanged, [=](float value) {
|
|
|
|
emit setPartDeformWidth(m_partId, value);
|
|
|
|
emit groupOperationAdded();
|
|
|
|
});
|
2022-10-18 09:35:04 +00:00
|
|
|
|
|
|
|
QPushButton* thicknessEraser = new QPushButton(QChar(fa::eraser));
|
2022-10-14 11:11:41 +00:00
|
|
|
Theme::initIconButton(thicknessEraser);
|
2022-10-18 09:35:04 +00:00
|
|
|
|
2022-10-14 11:11:41 +00:00
|
|
|
connect(thicknessEraser, &QPushButton::clicked, [=]() {
|
|
|
|
thicknessWidget->setValue(1.0);
|
|
|
|
emit groupOperationAdded();
|
|
|
|
});
|
2022-10-18 09:35:04 +00:00
|
|
|
|
|
|
|
QPushButton* widthEraser = new QPushButton(QChar(fa::eraser));
|
2022-10-14 11:11:41 +00:00
|
|
|
Theme::initIconButton(widthEraser);
|
2022-10-18 09:35:04 +00:00
|
|
|
|
2022-10-14 11:11:41 +00:00
|
|
|
connect(widthEraser, &QPushButton::clicked, [=]() {
|
|
|
|
widthWidget->setValue(1.0);
|
|
|
|
emit groupOperationAdded();
|
|
|
|
});
|
2022-10-18 09:35:04 +00:00
|
|
|
|
|
|
|
QVBoxLayout* deformLayout = new QVBoxLayout;
|
|
|
|
|
|
|
|
QHBoxLayout* thicknessLayout = new QHBoxLayout;
|
|
|
|
QHBoxLayout* widthLayout = new QHBoxLayout;
|
2022-10-14 11:11:41 +00:00
|
|
|
thicknessLayout->addWidget(thicknessEraser);
|
|
|
|
thicknessLayout->addWidget(thicknessWidget);
|
|
|
|
widthLayout->addWidget(widthEraser);
|
|
|
|
widthLayout->addWidget(widthWidget);
|
2022-10-18 09:35:04 +00:00
|
|
|
|
|
|
|
QCheckBox* deformUnifyStateBox = new QCheckBox();
|
2022-10-14 11:11:41 +00:00
|
|
|
Theme::initCheckbox(deformUnifyStateBox);
|
|
|
|
deformUnifyStateBox->setText(tr("Unified"));
|
|
|
|
deformUnifyStateBox->setChecked(m_part->deformUnified);
|
2022-10-18 09:35:04 +00:00
|
|
|
|
2022-10-14 11:11:41 +00:00
|
|
|
connect(deformUnifyStateBox, &QCheckBox::stateChanged, this, [=]() {
|
|
|
|
emit setPartDeformUnified(m_partId, deformUnifyStateBox->isChecked());
|
|
|
|
emit groupOperationAdded();
|
|
|
|
});
|
2022-10-18 09:35:04 +00:00
|
|
|
|
|
|
|
QHBoxLayout* deformUnifyLayout = new QHBoxLayout;
|
2022-10-14 11:11:41 +00:00
|
|
|
deformUnifyLayout->addStretch();
|
|
|
|
deformUnifyLayout->addWidget(deformUnifyStateBox);
|
|
|
|
|
|
|
|
deformLayout->addLayout(thicknessLayout);
|
|
|
|
deformLayout->addLayout(widthLayout);
|
|
|
|
deformLayout->addLayout(deformUnifyLayout);
|
2022-10-16 09:55:04 +00:00
|
|
|
|
|
|
|
deformGroupBox = new QGroupBox(tr("Deform"));
|
|
|
|
deformGroupBox->setLayout(deformLayout);
|
2022-10-14 11:11:41 +00:00
|
|
|
}
|
|
|
|
|
2022-10-18 09:35:04 +00:00
|
|
|
QGroupBox* cutFaceGroupBox = nullptr;
|
2022-10-14 11:11:41 +00:00
|
|
|
if (nullptr != m_part) {
|
2022-10-18 09:35:04 +00:00
|
|
|
FloatNumberWidget* rotationWidget = new FloatNumberWidget;
|
2022-10-14 11:11:41 +00:00
|
|
|
rotationWidget->setItemName(tr("Rotation"));
|
|
|
|
rotationWidget->setRange(-1, 1);
|
|
|
|
rotationWidget->setValue(m_part->cutRotation);
|
2022-10-18 09:35:04 +00:00
|
|
|
|
2022-10-14 11:11:41 +00:00
|
|
|
connect(rotationWidget, &FloatNumberWidget::valueChanged, [=](float value) {
|
|
|
|
emit setPartCutRotation(m_partId, value);
|
|
|
|
emit groupOperationAdded();
|
|
|
|
});
|
2022-10-18 09:35:04 +00:00
|
|
|
|
|
|
|
QPushButton* rotationEraser = new QPushButton(QChar(fa::eraser));
|
2022-10-14 11:11:41 +00:00
|
|
|
Theme::initIconButton(rotationEraser);
|
2022-10-18 09:35:04 +00:00
|
|
|
|
2022-10-14 11:11:41 +00:00
|
|
|
connect(rotationEraser, &QPushButton::clicked, [=]() {
|
|
|
|
rotationWidget->setValue(0.0);
|
|
|
|
emit groupOperationAdded();
|
|
|
|
});
|
2022-10-18 09:35:04 +00:00
|
|
|
|
|
|
|
QPushButton* rotationMinus5Button = new QPushButton(QChar(fa::rotateleft));
|
2022-10-14 11:11:41 +00:00
|
|
|
Theme::initIconButton(rotationMinus5Button);
|
2022-10-18 09:35:04 +00:00
|
|
|
|
2022-10-14 11:11:41 +00:00
|
|
|
connect(rotationMinus5Button, &QPushButton::clicked, [=]() {
|
|
|
|
rotationWidget->setValue(-0.5);
|
|
|
|
emit groupOperationAdded();
|
|
|
|
});
|
2022-10-18 09:35:04 +00:00
|
|
|
|
|
|
|
QPushButton* rotation5Button = new QPushButton(QChar(fa::rotateright));
|
2022-10-14 11:11:41 +00:00
|
|
|
Theme::initIconButton(rotation5Button);
|
2022-10-18 09:35:04 +00:00
|
|
|
|
2022-10-14 11:11:41 +00:00
|
|
|
connect(rotation5Button, &QPushButton::clicked, [=]() {
|
|
|
|
rotationWidget->setValue(0.5);
|
|
|
|
emit groupOperationAdded();
|
|
|
|
});
|
2022-10-16 09:55:04 +00:00
|
|
|
|
2022-10-18 09:35:04 +00:00
|
|
|
QHBoxLayout* rotationLayout = new QHBoxLayout;
|
2022-10-14 11:11:41 +00:00
|
|
|
rotationLayout->addWidget(rotationEraser);
|
|
|
|
rotationLayout->addWidget(rotationWidget);
|
|
|
|
rotationLayout->addWidget(rotationMinus5Button);
|
|
|
|
rotationLayout->addWidget(rotation5Button);
|
2022-10-16 09:55:04 +00:00
|
|
|
|
2022-10-18 09:35:04 +00:00
|
|
|
QCheckBox* subdivStateBox = new QCheckBox();
|
2022-10-16 09:55:04 +00:00
|
|
|
Theme::initCheckbox(subdivStateBox);
|
|
|
|
subdivStateBox->setText(tr("Subdivided"));
|
|
|
|
subdivStateBox->setChecked(m_part->subdived);
|
2022-10-18 09:35:04 +00:00
|
|
|
|
2022-10-16 09:55:04 +00:00
|
|
|
connect(subdivStateBox, &QCheckBox::stateChanged, this, [=]() {
|
|
|
|
emit setPartSubdivState(m_partId, subdivStateBox->isChecked());
|
|
|
|
emit groupOperationAdded();
|
|
|
|
});
|
|
|
|
|
2022-10-18 09:35:04 +00:00
|
|
|
QCheckBox* chamferStateBox = new QCheckBox();
|
2022-10-16 09:55:04 +00:00
|
|
|
Theme::initCheckbox(chamferStateBox);
|
|
|
|
chamferStateBox->setText(tr("Chamfered"));
|
|
|
|
chamferStateBox->setChecked(m_part->chamfered);
|
2022-10-18 09:35:04 +00:00
|
|
|
|
2022-10-16 09:55:04 +00:00
|
|
|
connect(chamferStateBox, &QCheckBox::stateChanged, this, [=]() {
|
|
|
|
emit setPartChamferState(m_partId, chamferStateBox->isChecked());
|
|
|
|
emit groupOperationAdded();
|
|
|
|
});
|
|
|
|
|
2022-10-18 09:35:04 +00:00
|
|
|
QCheckBox* roundEndStateBox = new QCheckBox();
|
2022-10-17 10:29:40 +00:00
|
|
|
Theme::initCheckbox(roundEndStateBox);
|
|
|
|
roundEndStateBox->setText(tr("Round end"));
|
|
|
|
roundEndStateBox->setChecked(m_part->rounded);
|
2022-10-18 09:35:04 +00:00
|
|
|
|
2022-10-17 10:29:40 +00:00
|
|
|
connect(roundEndStateBox, &QCheckBox::stateChanged, this, [=]() {
|
|
|
|
emit setPartRoundState(m_partId, roundEndStateBox->isChecked());
|
|
|
|
emit groupOperationAdded();
|
|
|
|
});
|
|
|
|
|
2022-10-18 09:35:04 +00:00
|
|
|
QHBoxLayout* optionsLayout = new QHBoxLayout;
|
2022-10-16 09:55:04 +00:00
|
|
|
optionsLayout->addStretch();
|
2022-10-17 10:29:40 +00:00
|
|
|
optionsLayout->addWidget(roundEndStateBox);
|
2022-10-16 09:55:04 +00:00
|
|
|
optionsLayout->addWidget(chamferStateBox);
|
|
|
|
optionsLayout->addWidget(subdivStateBox);
|
|
|
|
|
2022-10-18 09:35:04 +00:00
|
|
|
QVBoxLayout* cutFaceLayout = new QVBoxLayout;
|
2022-10-16 09:55:04 +00:00
|
|
|
cutFaceLayout->addLayout(rotationLayout);
|
|
|
|
cutFaceLayout->addLayout(optionsLayout);
|
|
|
|
|
|
|
|
cutFaceGroupBox = new QGroupBox(tr("Cut Face"));
|
|
|
|
cutFaceGroupBox->setLayout(cutFaceLayout);
|
2022-10-14 11:11:41 +00:00
|
|
|
}
|
|
|
|
|
2022-10-18 09:35:04 +00:00
|
|
|
QVBoxLayout* mainLayout = new QVBoxLayout;
|
2022-10-08 03:46:57 +00:00
|
|
|
mainLayout->addLayout(colorLayout);
|
2022-10-16 09:55:04 +00:00
|
|
|
if (nullptr != deformGroupBox)
|
|
|
|
mainLayout->addWidget(deformGroupBox);
|
|
|
|
if (nullptr != cutFaceGroupBox)
|
|
|
|
mainLayout->addWidget(cutFaceGroupBox);
|
2022-10-08 03:46:57 +00:00
|
|
|
mainLayout->setSizeConstraint(QLayout::SetFixedSize);
|
|
|
|
|
|
|
|
connect(this, &ComponentPropertyWidget::setPartColorState, m_document, &Document::setPartColorState);
|
|
|
|
connect(this, &ComponentPropertyWidget::endColorPicking, m_document, &Document::enableBackgroundBlur);
|
|
|
|
connect(this, &ComponentPropertyWidget::beginColorPicking, m_document, &Document::disableBackgroundBlur);
|
2022-10-14 11:11:41 +00:00
|
|
|
connect(this, &ComponentPropertyWidget::setPartDeformThickness, m_document, &Document::setPartDeformThickness);
|
|
|
|
connect(this, &ComponentPropertyWidget::setPartDeformWidth, m_document, &Document::setPartDeformWidth);
|
|
|
|
connect(this, &ComponentPropertyWidget::setPartDeformUnified, m_document, &Document::setPartDeformUnified);
|
|
|
|
connect(this, &ComponentPropertyWidget::setPartCutRotation, m_document, &Document::setPartCutRotation);
|
2022-10-16 09:55:04 +00:00
|
|
|
connect(this, &ComponentPropertyWidget::setPartSubdivState, m_document, &Document::setPartSubdivState);
|
|
|
|
connect(this, &ComponentPropertyWidget::setPartChamferState, m_document, &Document::setPartChamferState);
|
2022-10-17 10:29:40 +00:00
|
|
|
connect(this, &ComponentPropertyWidget::setPartRoundState, m_document, &Document::setPartRoundState);
|
2022-10-08 03:46:57 +00:00
|
|
|
connect(this, &ComponentPropertyWidget::groupOperationAdded, m_document, &Document::saveSnapshot);
|
|
|
|
|
|
|
|
setLayout(mainLayout);
|
|
|
|
|
|
|
|
setFixedSize(minimumSizeHint());
|
2022-10-08 01:05:53 +00:00
|
|
|
}
|
|
|
|
|
2022-10-08 03:46:57 +00:00
|
|
|
void ComponentPropertyWidget::preparePartIds()
|
2022-10-08 01:05:53 +00:00
|
|
|
{
|
2022-10-08 03:46:57 +00:00
|
|
|
std::unordered_set<dust3d::Uuid> addedPartIdSet;
|
2022-10-18 09:35:04 +00:00
|
|
|
for (const auto& componentId : m_componentIds) {
|
2022-10-08 03:46:57 +00:00
|
|
|
std::vector<dust3d::Uuid> partIds;
|
|
|
|
m_document->collectComponentDescendantParts(componentId, partIds);
|
2022-10-18 09:35:04 +00:00
|
|
|
for (const auto& it : partIds) {
|
2022-10-08 03:46:57 +00:00
|
|
|
if (addedPartIdSet.insert(it).second)
|
|
|
|
m_partIds.emplace_back(it);
|
|
|
|
}
|
|
|
|
}
|
2022-10-14 11:11:41 +00:00
|
|
|
if (1 == m_partIds.size()) {
|
|
|
|
m_part = m_document->findPart(m_partIds.front());
|
|
|
|
if (nullptr != m_part)
|
|
|
|
m_partId = m_partIds.front();
|
|
|
|
}
|
2022-10-08 03:46:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QColor ComponentPropertyWidget::lastColor()
|
|
|
|
{
|
|
|
|
QColor color = Qt::white;
|
2022-10-18 09:35:04 +00:00
|
|
|
std::map<QString, int> colorMap;
|
|
|
|
for (const auto& partId : m_partIds) {
|
|
|
|
const SkeletonPart* part = m_document->findPart(partId);
|
2022-10-08 03:46:57 +00:00
|
|
|
if (nullptr == part)
|
|
|
|
continue;
|
|
|
|
colorMap[part->color.name()]++;
|
|
|
|
}
|
|
|
|
if (!colorMap.empty()) {
|
2022-10-18 09:35:04 +00:00
|
|
|
color = std::max_element(colorMap.begin(), colorMap.end(),
|
|
|
|
[](const std::map<QString, int>::value_type& a, const std::map<QString, int>::value_type& b) {
|
|
|
|
return a.second < b.second;
|
|
|
|
})->first;
|
2022-10-08 03:46:57 +00:00
|
|
|
}
|
|
|
|
return color;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ComponentPropertyWidget::showColorDialog()
|
|
|
|
{
|
|
|
|
emit beginColorPicking();
|
|
|
|
QColor color = QColorDialog::getColor(m_color, this);
|
|
|
|
emit endColorPicking();
|
|
|
|
if (!color.isValid())
|
2022-10-08 01:05:53 +00:00
|
|
|
return;
|
2022-10-08 03:46:57 +00:00
|
|
|
|
2022-10-18 09:35:04 +00:00
|
|
|
for (const auto& partId : m_partIds) {
|
2022-10-08 03:46:57 +00:00
|
|
|
emit setPartColorState(partId, true, color);
|
2022-10-08 01:05:53 +00:00
|
|
|
}
|
2022-10-08 03:46:57 +00:00
|
|
|
emit groupOperationAdded();
|
2022-10-08 01:05:53 +00:00
|
|
|
}
|