增加预览页介绍信息显示
This commit is contained in:
@@ -3,14 +3,18 @@
|
||||
#include "dialogs/CancelableTaskDialog.h"
|
||||
#include "editor/EditorCanvas.h"
|
||||
#include "dialogs/ImageCropDialog.h"
|
||||
#include "core/domain/EntityIntro.h"
|
||||
#include "core/net/ModelServerClient.h"
|
||||
#include "params/ParamControls.h"
|
||||
#include "props/BackgroundPropertySection.h"
|
||||
#include "props/EntityPropertySection.h"
|
||||
#include "timeline/TimelineWidget.h"
|
||||
#include "dialogs/FrameAnimationDialog.h"
|
||||
#include "dialogs/EntityIntroPopup.h"
|
||||
|
||||
#include <QAbstractItemView>
|
||||
#include <QAbstractSpinBox>
|
||||
#include <QApplication>
|
||||
#include <QAction>
|
||||
#include <QBoxLayout>
|
||||
#include <QButtonGroup>
|
||||
@@ -43,7 +47,10 @@
|
||||
#include <QFile>
|
||||
#include <QIcon>
|
||||
#include <QResizeEvent>
|
||||
#include <QScrollArea>
|
||||
#include <QShowEvent>
|
||||
#include <QtGlobal>
|
||||
#include <QWheelEvent>
|
||||
#include <QSizePolicy>
|
||||
#include <QStyle>
|
||||
#include <QUrl>
|
||||
@@ -130,6 +137,31 @@ const char* kFloatingModeDockQss = R"(
|
||||
}
|
||||
)";
|
||||
|
||||
/// 避免滚轮先被 QScrollArea 吃掉,导致内嵌 QDoubleSpinBox 无法用滚轮调节
|
||||
class SpinFriendlyScrollArea final : public QScrollArea {
|
||||
public:
|
||||
explicit SpinFriendlyScrollArea(QWidget* parent = nullptr)
|
||||
: QScrollArea(parent) {}
|
||||
|
||||
protected:
|
||||
void wheelEvent(QWheelEvent* e) override {
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
const QPoint inVp = viewport()->mapFrom(this, e->position().toPoint());
|
||||
#else
|
||||
const QPoint inVp = viewport()->mapFrom(this, e->pos());
|
||||
#endif
|
||||
if (QWidget* w = viewport()->childAt(inVp)) {
|
||||
for (QWidget* cur = w; cur; cur = cur->parentWidget()) {
|
||||
if (qobject_cast<QAbstractSpinBox*>(cur)) {
|
||||
QApplication::sendEvent(cur, e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
QScrollArea::wheelEvent(e);
|
||||
}
|
||||
};
|
||||
|
||||
const char* kTimelineBarQss = R"(
|
||||
#TimelineDockBar QToolButton, #TimelineDockBar QPushButton {
|
||||
border: 1px solid palette(midlight);
|
||||
@@ -158,6 +190,7 @@ public:
|
||||
EditorCanvas* canvas = nullptr;
|
||||
QWidget* modeDock = nullptr;
|
||||
QWidget* toolDock = nullptr;
|
||||
QWidget* previewPlaybackBar = nullptr;
|
||||
|
||||
void relayoutFloaters() {
|
||||
if (canvas) {
|
||||
@@ -201,6 +234,18 @@ public:
|
||||
if (toolDock && toolDock->isVisible()) {
|
||||
toolDock->raise();
|
||||
}
|
||||
|
||||
if (previewPlaybackBar && previewPlaybackBar->isVisible()) {
|
||||
if (QLayout* lay = previewPlaybackBar->layout()) {
|
||||
lay->activate();
|
||||
}
|
||||
previewPlaybackBar->updateGeometry();
|
||||
previewPlaybackBar->adjustSize();
|
||||
const int x = std::max(kMargin, (width() - previewPlaybackBar->width()) / 2);
|
||||
const int y = std::max(kMargin, height() - previewPlaybackBar->height() - kMargin);
|
||||
previewPlaybackBar->move(x, y);
|
||||
previewPlaybackBar->raise();
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
@@ -229,6 +274,21 @@ MainWindow::MainWindow(QWidget* parent)
|
||||
createMenus();
|
||||
createProjectTreeDock();
|
||||
createTimelineDock();
|
||||
|
||||
if (m_previewBtnPlay && m_previewBtnPause && m_btnPlay) {
|
||||
connect(m_previewBtnPlay, &QToolButton::clicked, this, [this]() {
|
||||
if (m_btnPlay && !m_btnPlay->isChecked()) {
|
||||
m_btnPlay->setChecked(true);
|
||||
}
|
||||
});
|
||||
connect(m_previewBtnPause, &QToolButton::clicked, this, [this]() {
|
||||
if (m_btnPlay && m_btnPlay->isChecked()) {
|
||||
m_btnPlay->setChecked(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
syncPreviewPlaybackBar();
|
||||
|
||||
refreshProjectTree();
|
||||
updateUiEnabledState();
|
||||
refreshEditorPage();
|
||||
@@ -604,6 +664,15 @@ void MainWindow::onTogglePlay(bool on) {
|
||||
m_playTimer->stop();
|
||||
}
|
||||
}
|
||||
syncPreviewPlaybackBar();
|
||||
}
|
||||
|
||||
void MainWindow::syncPreviewPlaybackBar() {
|
||||
if (!m_previewBtnPlay || !m_previewBtnPause) {
|
||||
return;
|
||||
}
|
||||
m_previewBtnPlay->setEnabled(!m_playing);
|
||||
m_previewBtnPause->setEnabled(m_playing);
|
||||
}
|
||||
|
||||
void MainWindow::onInsertCombinedKey() {
|
||||
@@ -860,7 +929,14 @@ void MainWindow::createProjectTreeDock() {
|
||||
}
|
||||
});
|
||||
connect(m_projectTree, &QTreeWidget::itemClicked, this, &MainWindow::onProjectTreeItemClicked);
|
||||
dockLayout->addWidget(m_projectTree, 1);
|
||||
|
||||
auto* treeScroll = new SpinFriendlyScrollArea(dockContent);
|
||||
treeScroll->setWidgetResizable(true);
|
||||
treeScroll->setFrameShape(QFrame::NoFrame);
|
||||
treeScroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
treeScroll->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
treeScroll->setWidget(m_projectTree);
|
||||
dockLayout->addWidget(treeScroll, 1);
|
||||
|
||||
m_dockProjectTree->setWidget(dockContent);
|
||||
m_dockProjectTree->installEventFilter(this);
|
||||
@@ -875,7 +951,7 @@ void MainWindow::createProjectTreeDock() {
|
||||
|
||||
m_bgPropertySection = new gui::BackgroundPropertySection();
|
||||
m_entityPropertySection = new gui::EntityPropertySection();
|
||||
m_propertyStack = new QStackedWidget(m_dockProperties);
|
||||
m_propertyStack = new QStackedWidget();
|
||||
m_propertyStack->setContentsMargins(4, 4, 4, 4);
|
||||
m_propertyStack->addWidget(m_bgPropertySection);
|
||||
m_propertyStack->addWidget(m_entityPropertySection);
|
||||
@@ -927,11 +1003,48 @@ void MainWindow::createProjectTreeDock() {
|
||||
});
|
||||
connect(m_entityPropertySection, &gui::EntityPropertySection::userScaleEdited, this, [this](double v) {
|
||||
if (m_selectedEntityId.isEmpty()) return;
|
||||
if (!m_workspace.setEntityUserScale(m_selectedEntityId, v)) return;
|
||||
if (!m_workspace.setEntityUserScale(m_selectedEntityId, v, m_currentFrame)) return;
|
||||
refreshEditorPage();
|
||||
refreshDopeSheet();
|
||||
});
|
||||
connect(m_entityPropertySection, &gui::EntityPropertySection::introContentEdited, this, [this]() {
|
||||
if (m_selectedEntityId.isEmpty() || !m_entityPropertySection) {
|
||||
return;
|
||||
}
|
||||
const core::EntityIntroContent intro = m_entityPropertySection->introSnapshot();
|
||||
if (!m_workspace.setEntityIntroContent(m_selectedEntityId, intro)) {
|
||||
QMessageBox::warning(this, QStringLiteral("介绍"), QStringLiteral("自动保存失败。"));
|
||||
return;
|
||||
}
|
||||
refreshEditorPage();
|
||||
});
|
||||
connect(m_entityPropertySection, &gui::EntityPropertySection::introAddImageRequested, this, [this]() {
|
||||
if (m_selectedEntityId.isEmpty() || !m_entityPropertySection) {
|
||||
return;
|
||||
}
|
||||
const QString path = QFileDialog::getOpenFileName(
|
||||
this,
|
||||
QStringLiteral("选择配图"),
|
||||
{},
|
||||
QStringLiteral("图片 (*.png *.jpg *.jpeg *.webp *.bmp);;所有文件 (*)"));
|
||||
if (path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
QString rel;
|
||||
if (!m_workspace.importEntityIntroImageFromFile(m_selectedEntityId, path, &rel)) {
|
||||
QMessageBox::warning(this, QStringLiteral("介绍"), QStringLiteral("导入配图失败。"));
|
||||
return;
|
||||
}
|
||||
m_entityPropertySection->appendIntroImagePath(rel);
|
||||
});
|
||||
|
||||
m_dockProperties->setWidget(m_propertyStack);
|
||||
auto* propScroll = new SpinFriendlyScrollArea(m_dockProperties);
|
||||
propScroll->setWidgetResizable(true);
|
||||
propScroll->setFrameShape(QFrame::NoFrame);
|
||||
propScroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
propScroll->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
propScroll->setWidget(m_propertyStack);
|
||||
m_dockProperties->setWidget(propScroll);
|
||||
|
||||
addDockWidget(Qt::RightDockWidgetArea, m_dockProjectTree);
|
||||
splitDockWidget(m_dockProjectTree, m_dockProperties, Qt::Vertical);
|
||||
@@ -996,10 +1109,12 @@ void MainWindow::refreshPropertyPanel() {
|
||||
|
||||
QString displayName;
|
||||
double userScale = 1.0;
|
||||
core::EntityIntroContent intro;
|
||||
for (const auto& e : m_workspace.entities()) {
|
||||
if (e.id == m_selectedEntityId) {
|
||||
displayName = e.displayName;
|
||||
userScale = e.userScale;
|
||||
intro = e.intro;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1013,6 +1128,7 @@ void MainWindow::refreshPropertyPanel() {
|
||||
st.pivot = m_editorCanvas->selectedAnimatedOriginWorld();
|
||||
st.centroid = m_editorCanvas->selectedEntityCentroidWorld();
|
||||
st.userScale = userScale;
|
||||
st.intro = intro;
|
||||
m_entityPropertySection->applyState(st);
|
||||
m_propertyStack->setCurrentWidget(m_entityPropertySection);
|
||||
m_dockProperties->setWindowTitle(QStringLiteral("属性 — 实体"));
|
||||
@@ -1323,6 +1439,9 @@ void MainWindow::applyUiMode(UiMode mode) {
|
||||
if (m_floatingToolDock) {
|
||||
m_floatingToolDock->setVisible(projectOpen && !preview);
|
||||
}
|
||||
if (m_previewPlaybackBar) {
|
||||
m_previewPlaybackBar->setVisible(projectOpen && preview);
|
||||
}
|
||||
if (m_canvasHost) {
|
||||
m_canvasHost->updateGeometry();
|
||||
static_cast<CanvasHost*>(m_canvasHost)->relayoutFloaters();
|
||||
@@ -1500,6 +1619,28 @@ void MainWindow::rebuildCentralPages() {
|
||||
m_editorCanvas = new EditorCanvas(canvasHost);
|
||||
canvasHost->canvas = m_editorCanvas;
|
||||
|
||||
m_entityIntroPopup = new gui::EntityIntroPopup(this);
|
||||
|
||||
m_previewPlaybackBar = new QFrame(canvasHost);
|
||||
m_previewPlaybackBar->setObjectName(QStringLiteral("PreviewPlaybackBar"));
|
||||
m_previewPlaybackBar->setStyleSheet(QString::fromUtf8(kTimelineBarQss));
|
||||
auto* pbl = new QHBoxLayout(m_previewPlaybackBar);
|
||||
pbl->setContentsMargins(10, 6, 10, 6);
|
||||
pbl->setSpacing(8);
|
||||
m_previewBtnPlay = new QToolButton(m_previewPlaybackBar);
|
||||
m_previewBtnPlay->setText(QStringLiteral("播放"));
|
||||
m_previewBtnPlay->setToolTip(QStringLiteral("播放时间轴"));
|
||||
m_previewBtnPause = new QToolButton(m_previewPlaybackBar);
|
||||
m_previewBtnPause->setText(QStringLiteral("暂停"));
|
||||
m_previewBtnPause->setToolTip(QStringLiteral("暂停时间轴"));
|
||||
polishCompactToolButton(m_previewBtnPlay, 36);
|
||||
polishCompactToolButton(m_previewBtnPause, 36);
|
||||
pbl->addWidget(m_previewBtnPlay);
|
||||
pbl->addWidget(m_previewBtnPause);
|
||||
m_previewPlaybackBar->setParent(canvasHost);
|
||||
m_previewPlaybackBar->hide();
|
||||
canvasHost->previewPlaybackBar = m_previewPlaybackBar;
|
||||
|
||||
m_floatingModeDock = new QFrame(canvasHost);
|
||||
m_floatingModeDock->setObjectName(QStringLiteral("FloatingModeDock"));
|
||||
m_floatingModeDock->setFrameShape(QFrame::NoFrame);
|
||||
@@ -1685,6 +1826,33 @@ void MainWindow::rebuildCentralPages() {
|
||||
updateUiEnabledState();
|
||||
});
|
||||
|
||||
connect(m_editorCanvas, &EditorCanvas::presentationEntityIntroRequested, this,
|
||||
[this](const QString& id, QPointF anchorView) {
|
||||
if (!m_entityIntroPopup || !m_workspace.isOpen()) {
|
||||
return;
|
||||
}
|
||||
const core::Project::Entity* hit = nullptr;
|
||||
for (const auto& e : m_workspace.entities()) {
|
||||
if (e.id == id) {
|
||||
hit = &e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_entityIntroPopup->setProjectDir(m_workspace.projectDir());
|
||||
if (hit) {
|
||||
m_entityIntroPopup->setContent(hit->intro);
|
||||
} else {
|
||||
core::EntityIntroContent empty;
|
||||
m_entityIntroPopup->setContent(empty);
|
||||
}
|
||||
m_entityIntroPopup->showNearCanvasPoint(anchorView.toPoint(), m_editorCanvas);
|
||||
});
|
||||
connect(m_editorCanvas, &EditorCanvas::presentationInteractionDismissed, this, [this]() {
|
||||
if (m_entityIntroPopup) {
|
||||
m_entityIntroPopup->clearAndHide();
|
||||
}
|
||||
});
|
||||
|
||||
connect(group, &QButtonGroup::idClicked, this, [this](int id) {
|
||||
if (!m_editorCanvas) {
|
||||
return;
|
||||
@@ -1793,6 +1961,12 @@ void MainWindow::setPreviewRequested(bool preview) {
|
||||
return;
|
||||
}
|
||||
m_previewRequested = preview;
|
||||
if (!preview && m_editorCanvas) {
|
||||
m_editorCanvas->clearPresentationEntityFocus();
|
||||
}
|
||||
if (!preview && m_entityIntroPopup) {
|
||||
m_entityIntroPopup->clearAndHide();
|
||||
}
|
||||
updateUiEnabledState();
|
||||
if (preview) {
|
||||
refreshPreviewPage();
|
||||
|
||||
Reference in New Issue
Block a user