Files
hfut-bishe/client/gui/props/EntityPropertySection.cpp
2026-04-09 23:13:33 +08:00

364 lines
14 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "props/EntityPropertySection.h"
#include "params/ParamControls.h"
#include <QDoubleSpinBox>
#include <QFormLayout>
#include <QHBoxLayout>
#include <QCheckBox>
#include <QLabel>
#include <QLineEdit>
#include <QListWidget>
#include <QPushButton>
#include <QTextEdit>
#include <QTimer>
#include <QToolButton>
#include <QVBoxLayout>
namespace gui {
EntityPropertySection::EntityPropertySection(QWidget* parent)
: PropertySectionWidget(parent) {
auto* lay = new QVBoxLayout(this);
lay->setContentsMargins(0, 0, 0, 0);
lay->setSpacing(6);
auto* form = new QFormLayout();
form->setContentsMargins(0, 0, 0, 0);
form->setSpacing(6);
m_name = new QLineEdit(this);
m_name->setPlaceholderText(QStringLiteral("显示名称"));
m_name->setToolTip(QStringLiteral("仅显示用;内部 id 不变"));
form->addRow(QStringLiteral("名称"), m_name);
m_depth = new QLabel(QStringLiteral("-"), this);
m_distScale = new QLabel(QStringLiteral("-"), this);
for (QLabel* lab : {m_depth, m_distScale}) {
lab->setTextInteractionFlags(Qt::TextSelectableByMouse);
}
form->addRow(QStringLiteral("深度"), m_depth);
form->addRow(QStringLiteral("距离缩放"), m_distScale);
m_pivotLabel = new QLabel(QStringLiteral("中心坐标"), this);
m_pivot = new Vec2ParamControl(this);
m_pivot->setToolTip(QStringLiteral("枢轴在世界坐标中的位置(限制在轮廓包络内),用于重定位局部原点"));
form->addRow(m_pivotLabel, m_pivot);
m_centroidLabel = new QLabel(QStringLiteral("位置"), this);
m_centroid = new Vec2ParamControl(this);
m_centroid->setToolTip(QStringLiteral("实体几何质心的世界坐标;修改将整体平移实体"));
form->addRow(m_centroidLabel, m_centroid);
m_userScale = new QDoubleSpinBox(this);
m_userScale->setRange(0.05, 20.0);
m_userScale->setDecimals(3);
m_userScale->setSingleStep(0.05);
m_userScale->setValue(1.0);
m_userScale->setToolTip(QStringLiteral("人为整体缩放,与深度距离缩放相乘"));
form->addRow(QStringLiteral("整体缩放"), m_userScale);
m_ignoreDistanceScale = new QCheckBox(QStringLiteral("不受距离缩放影响"), this);
m_ignoreDistanceScale->setToolTip(QStringLiteral("开启后实体不受深度驱动的距离缩放影响,仅受整体缩放影响(对话气泡默认开启)"));
form->addRow(QStringLiteral("距离缩放"), m_ignoreDistanceScale);
m_visible = new QCheckBox(QString(), this);
m_visible->setChecked(true);
m_visible->setToolTip(QStringLiteral("随帧变化在当前帧切换会写入可见性关键帧10帧淡入淡出"));
form->addRow(QStringLiteral("可见性"), m_visible);
lay->addLayout(form);
m_introHeader = new QWidget(this);
auto* headLay = new QHBoxLayout(m_introHeader);
headLay->setContentsMargins(0, 2, 0, 2);
headLay->setSpacing(6);
m_introToggle = new QToolButton(m_introHeader);
m_introToggle->setAutoRaise(true);
m_introToggle->setToolButtonStyle(Qt::ToolButtonTextOnly);
m_introToggle->setCursor(Qt::PointingHandCursor);
m_introToggle->setToolTip(QStringLiteral("展开或折叠介绍设置"));
m_introToggle->setText(QStringLiteral(""));
m_introToggle->setFixedWidth(22);
auto* introTitleLab = new QLabel(QStringLiteral("介绍"), m_introHeader);
introTitleLab->setStyleSheet(QStringLiteral("QLabel { font-weight: 600; }"));
headLay->addWidget(m_introToggle, 0, Qt::AlignVCenter);
headLay->addWidget(introTitleLab, 1, Qt::AlignVCenter);
m_introContent = new QWidget(this);
auto* introLay = new QVBoxLayout(m_introContent);
introLay->setContentsMargins(8, 4, 0, 0);
introLay->setSpacing(6);
m_introTitle = new QLineEdit(m_introContent);
m_introTitle->setPlaceholderText(QStringLiteral("标题"));
introLay->addWidget(new QLabel(QStringLiteral("标题"), m_introContent));
introLay->addWidget(m_introTitle);
m_introBody = new QTextEdit(m_introContent);
m_introBody->setPlaceholderText(QStringLiteral("正文(纯文本)"));
m_introBody->setMaximumHeight(100);
introLay->addWidget(new QLabel(QStringLiteral("正文"), m_introContent));
introLay->addWidget(m_introBody);
m_introImages = new QListWidget(m_introContent);
m_introImages->setMinimumHeight(72);
m_introImages->setToolTip(QStringLiteral("配图相对路径列表;使用下方按钮从文件添加"));
introLay->addWidget(new QLabel(QStringLiteral("配图"), m_introContent));
introLay->addWidget(m_introImages);
auto* imgRow = new QHBoxLayout();
m_btnIntroAddImage = new QPushButton(QStringLiteral("添加配图…"), m_introContent);
m_btnIntroRemoveImage = new QPushButton(QStringLiteral("移除选中"), m_introContent);
imgRow->addWidget(m_btnIntroAddImage);
imgRow->addWidget(m_btnIntroRemoveImage);
introLay->addLayout(imgRow);
m_introVideo = new QLineEdit(m_introContent);
m_introVideo->setPlaceholderText(QStringLiteral("可选:视频相对路径(如 assets/entities/xxx.mp4"));
introLay->addWidget(new QLabel(QStringLiteral("视频路径(预留)"), m_introContent));
introLay->addWidget(m_introVideo);
lay->addWidget(m_introHeader);
lay->addWidget(m_introContent);
lay->addStretch(1);
m_introContent->setVisible(false);
m_introSaveTimer = new QTimer(this);
m_introSaveTimer->setSingleShot(true);
connect(m_introSaveTimer, &QTimer::timeout, this, [this]() {
if (!m_introBulkUpdate) {
emit introContentEdited();
}
});
connect(m_introToggle, &QToolButton::clicked, this, [this]() {
setIntroSectionExpanded(!m_introContent->isVisible());
});
connect(m_name, &QLineEdit::editingFinished, this, [this]() {
if (m_name) {
emit displayNameCommitted(m_name->text());
}
});
connect(m_pivot, &Vec2ParamControl::valueChanged, this, &EntityPropertySection::pivotEdited);
connect(m_centroid, &Vec2ParamControl::valueChanged, this, &EntityPropertySection::centroidEdited);
connect(m_userScale, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &EntityPropertySection::userScaleEdited);
connect(m_ignoreDistanceScale, &QCheckBox::toggled, this, &EntityPropertySection::ignoreDistanceScaleToggled);
connect(m_visible, &QCheckBox::toggled, this, &EntityPropertySection::visibleToggled);
connect(m_introTitle, &QLineEdit::textChanged, this, [this](const QString&) { scheduleIntroPersist(); });
connect(m_introBody, &QTextEdit::textChanged, this, [this]() { scheduleIntroPersist(); });
connect(m_introVideo, &QLineEdit::textChanged, this, [this](const QString&) { scheduleIntroPersist(); });
connect(m_btnIntroAddImage, &QPushButton::clicked, this, &EntityPropertySection::introAddImageRequested);
connect(m_btnIntroRemoveImage, &QPushButton::clicked, this, [this]() {
if (!m_introImages || !m_introImages->currentItem()) {
return;
}
delete m_introImages->takeItem(m_introImages->currentRow());
scheduleIntroPersist();
});
}
void EntityPropertySection::setIntroSectionExpanded(bool expanded) {
if (!m_introContent || !m_introToggle) {
return;
}
m_introContent->setVisible(expanded);
m_introToggle->setText(expanded ? QStringLiteral("") : QStringLiteral(""));
}
void EntityPropertySection::scheduleIntroPersist() {
if (m_introBulkUpdate || !m_introSaveTimer) {
return;
}
m_introSaveTimer->start(400);
}
void EntityPropertySection::clearDisconnected() {
if (m_introSaveTimer) {
m_introSaveTimer->stop();
}
m_introBulkUpdate = true;
setEditingEnabled(false);
if (m_name) {
m_name->blockSignals(true);
m_name->clear();
m_name->blockSignals(false);
}
if (m_depth) m_depth->setText(QStringLiteral("-"));
if (m_distScale) m_distScale->setText(QStringLiteral("-"));
if (m_pivot) m_pivot->setValue(0.0, 0.0);
if (m_centroid) m_centroid->setValue(0.0, 0.0);
if (m_pivotLabel) m_pivotLabel->setText(QStringLiteral("中心坐标"));
if (m_centroidLabel) m_centroidLabel->setText(QStringLiteral("位置"));
if (m_pivot) {
m_pivot->setToolTip(QStringLiteral("枢轴在世界坐标中的位置(限制在轮廓包络内),用于重定位局部原点"));
}
if (m_centroid) {
m_centroid->setToolTip(QStringLiteral("实体几何质心的世界坐标;修改将整体平移实体"));
}
if (m_userScale) {
m_userScale->blockSignals(true);
m_userScale->setValue(1.0);
m_userScale->blockSignals(false);
}
if (m_ignoreDistanceScale) {
m_ignoreDistanceScale->blockSignals(true);
m_ignoreDistanceScale->setChecked(false);
m_ignoreDistanceScale->blockSignals(false);
}
if (m_visible) {
m_visible->blockSignals(true);
m_visible->setChecked(true);
m_visible->blockSignals(false);
}
if (m_introTitle) {
m_introTitle->blockSignals(true);
m_introTitle->clear();
m_introTitle->blockSignals(false);
}
if (m_introBody) {
m_introBody->blockSignals(true);
m_introBody->clear();
m_introBody->blockSignals(false);
}
if (m_introVideo) {
m_introVideo->blockSignals(true);
m_introVideo->clear();
m_introVideo->blockSignals(false);
}
if (m_introImages) m_introImages->clear();
setIntroSectionExpanded(false);
m_introBulkUpdate = false;
}
void EntityPropertySection::applyState(const EntityPropertyUiState& s) {
if (m_introSaveTimer) {
m_introSaveTimer->stop();
}
m_introBulkUpdate = true;
setEditingEnabled(true);
if (m_name) {
m_name->blockSignals(true);
m_name->setText(s.displayName);
m_name->blockSignals(false);
}
if (m_depth) m_depth->setText(QString::number(s.depthZ));
if (m_distScale) m_distScale->setText(s.distanceScaleText);
if (m_pivotLabel) {
m_pivotLabel->setText(QStringLiteral("中心坐标"));
}
if (m_centroidLabel) {
m_centroidLabel->setText(QStringLiteral("位置"));
}
if (m_pivot) {
m_pivot->setToolTip(
s.parentRelativeMode
? QStringLiteral("枢轴相对父对象的坐标;修改将写入相对父对象的位置关键帧")
: QStringLiteral("枢轴在世界坐标中的位置(限制在轮廓包络内),用于重定位局部原点"));
}
if (m_centroid) {
m_centroid->setToolTip(
s.parentRelativeMode
? QStringLiteral("几何质心相对父对象的坐标;修改将写入相对父对象的位置关键帧")
: QStringLiteral("实体几何质心的世界坐标;修改将整体平移实体"));
}
if (m_pivot) m_pivot->setValue(s.pivot.x(), s.pivot.y());
if (m_centroid) m_centroid->setValue(s.centroid.x(), s.centroid.y());
if (m_userScale) {
m_userScale->blockSignals(true);
m_userScale->setValue(s.userScale);
m_userScale->blockSignals(false);
}
if (m_ignoreDistanceScale) {
m_ignoreDistanceScale->blockSignals(true);
m_ignoreDistanceScale->setChecked(s.ignoreDistanceScale);
m_ignoreDistanceScale->blockSignals(false);
}
if (m_visible) {
m_visible->blockSignals(true);
m_visible->setChecked(s.visible);
m_visible->blockSignals(false);
}
if (m_introTitle) {
m_introTitle->blockSignals(true);
m_introTitle->setText(s.intro.title);
m_introTitle->blockSignals(false);
}
if (m_introBody) {
m_introBody->blockSignals(true);
m_introBody->setPlainText(s.intro.bodyText);
m_introBody->blockSignals(false);
}
if (m_introVideo) {
m_introVideo->blockSignals(true);
m_introVideo->setText(s.intro.videoPathRelative);
m_introVideo->blockSignals(false);
}
if (m_introImages) {
m_introImages->clear();
for (const QString& p : s.intro.imagePathsRelative) {
if (!p.isEmpty()) {
auto* it = new QListWidgetItem(p, m_introImages);
it->setData(Qt::UserRole, p);
}
}
}
setIntroSectionExpanded(false);
m_introBulkUpdate = false;
}
void EntityPropertySection::appendIntroImagePath(const QString& relativePath) {
if (!m_introImages || relativePath.isEmpty()) {
return;
}
auto* it = new QListWidgetItem(relativePath, m_introImages);
it->setData(Qt::UserRole, relativePath);
scheduleIntroPersist();
}
core::EntityIntroContent EntityPropertySection::introSnapshot() const {
core::EntityIntroContent out;
if (m_introTitle) {
out.title = m_introTitle->text();
}
if (m_introBody) {
out.bodyText = m_introBody->toPlainText();
}
if (m_introVideo) {
out.videoPathRelative = m_introVideo->text().trimmed();
}
if (m_introImages) {
for (int i = 0; i < m_introImages->count(); ++i) {
const QString p = m_introImages->item(i)->data(Qt::UserRole).toString();
if (!p.isEmpty()) {
out.imagePathsRelative.push_back(p);
}
}
}
return out;
}
void EntityPropertySection::setEditingEnabled(bool on) {
if (m_name) m_name->setEnabled(on);
if (m_pivot) m_pivot->setEnabled(on);
if (m_centroid) m_centroid->setEnabled(on);
if (m_userScale) m_userScale->setEnabled(on);
if (m_ignoreDistanceScale) m_ignoreDistanceScale->setEnabled(on);
if (m_visible) m_visible->setEnabled(on);
if (m_introHeader) m_introHeader->setEnabled(on);
if (m_introToggle) m_introToggle->setEnabled(on);
if (m_introTitle) m_introTitle->setEnabled(on);
if (m_introBody) m_introBody->setEnabled(on);
if (m_introVideo) m_introVideo->setEnabled(on);
if (m_introImages) m_introImages->setEnabled(on);
if (m_btnIntroAddImage) m_btnIntroAddImage->setEnabled(on);
if (m_btnIntroRemoveImage) m_btnIntroRemoveImage->setEnabled(on);
}
} // namespace gui