Files
hfut-bishe/client/gui/props/EntityPropertySection.cpp

302 lines
11 KiB
C++
Raw 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 <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_pivot = new Vec2ParamControl(this);
m_pivot->setToolTip(QStringLiteral("枢轴在世界坐标中的位置(限制在轮廓包络内),用于重定位局部原点"));
form->addRow(QStringLiteral("中心坐标"), m_pivot);
m_centroid = new Vec2ParamControl(this);
m_centroid->setToolTip(QStringLiteral("实体几何质心的世界坐标;修改将整体平移实体"));
form->addRow(QStringLiteral("位置"), 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);
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_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_userScale) {
m_userScale->blockSignals(true);
m_userScale->setValue(1.0);
m_userScale->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_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_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_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