This commit is contained in:
2026-04-09 23:13:33 +08:00
parent d67d7dc0c5
commit 6cb82cec57
24 changed files with 1733 additions and 71 deletions

View File

@@ -2,6 +2,7 @@
#include "dialogs/AboutWindow.h"
#include "dialogs/CancelableTaskDialog.h"
#include "dialogs/EntityFinalizeDialog.h"
#include "dialogs/BlackholeResolveDialog.h"
#include "editor/EditorCanvas.h"
#include "editor/EntityCutoutUtils.h"
#include "dialogs/ImageCropDialog.h"
@@ -10,6 +11,7 @@
#include "widgets/ToolOptionPopup.h"
#include "params/ParamControls.h"
#include "props/BackgroundPropertySection.h"
#include "props/BlackholePropertySection.h"
#include "props/EntityPropertySection.h"
#include "props/ToolPropertySection.h"
#include "timeline/TimelineWidget.h"
@@ -87,9 +89,9 @@ constexpr int kRightDockMinimumWidth = 80;
/// 列宽小于此值时自动隐藏右侧两 dock
constexpr int kRightDockAutoHideBelow = 92;
/// 右侧 dock 列最大宽度,避免过宽挤占画布
constexpr int kRightDockMaximumWidth = 288;
constexpr int kRightDockMaximumWidth = 252;
/// 属性区表单内容最大宽度dock 仍可略宽,两侧留白,避免 SpinBox 被拉得过开)
constexpr int kPropertyPanelContentMaxWidth = 268;
constexpr int kPropertyPanelContentMaxWidth = 232;
/// 启动时垂直分割高度:项目树较矮、属性区较高
constexpr int kProjectTreeDockStartupHeight = 148;
constexpr int kPropertiesDockStartupHeight = 392;
@@ -1185,6 +1187,14 @@ void MainWindow::createProjectTreeDock() {
showBackgroundContextMenu(m_projectTree->viewport()->mapToGlobal(pos));
return;
}
const QString kind = item->data(0, Qt::UserRole).toString();
if (kind == QStringLiteral("blackhole")) {
const QString id = item->data(0, Qt::UserRole + 1).toString();
if (!id.isEmpty()) {
showBlackholeContextMenu(m_projectTree->viewport()->mapToGlobal(pos), id);
}
return;
}
});
connect(m_projectTree, &QTreeWidget::itemClicked, this, &MainWindow::onProjectTreeItemClicked);
static_cast<ProjectTreeWidget*>(m_projectTree)->onNodeParentDropRequested =
@@ -1280,15 +1290,17 @@ void MainWindow::createProjectTreeDock() {
Qt::BottomDockWidgetArea | Qt::TopDockWidgetArea);
m_dockProperties->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable |
QDockWidget::DockWidgetClosable);
m_dockProperties->setMinimumWidth(236);
m_dockProperties->setMinimumWidth(200);
m_bgPropertySection = new gui::BackgroundPropertySection();
m_blackholePropertySection = new gui::BlackholePropertySection();
m_entityPropertySection = new gui::EntityPropertySection();
m_toolPropertySection = new gui::ToolPropertySection();
m_propertyStack = new QStackedWidget();
m_propertyStack->setContentsMargins(4, 4, 4, 4);
m_propertyStack->setMaximumWidth(kPropertyPanelContentMaxWidth);
m_propertyStack->addWidget(m_bgPropertySection);
m_propertyStack->addWidget(m_blackholePropertySection);
m_propertyStack->addWidget(m_entityPropertySection);
m_propertyStack->addWidget(m_toolPropertySection);
@@ -1336,7 +1348,30 @@ void MainWindow::createProjectTreeDock() {
if (m_selectedEntityId.isEmpty() || !m_editorCanvas) return;
const double s = m_editorCanvas->selectedCombinedScale();
if (s <= 1e-9) return;
if (!m_workspace.reanchorEntityPivot(m_selectedEntityId, m_currentFrame, QPointF(x, y), s)) return;
QPointF targetPivot(x, y);
QString parentId;
for (const auto& e : m_workspace.entities()) {
if (e.id == m_selectedEntityId) {
parentId = e.parentId;
break;
}
}
if (!parentId.isEmpty()) {
const auto rf = core::eval::evaluateAtFrame(m_workspace.project(), m_currentFrame, 10);
for (const auto& pe : rf.entities) {
if (pe.entity.id == parentId) {
targetPivot += pe.entity.originWorld;
break;
}
}
for (const auto& pt : rf.tools) {
if (pt.tool.id == parentId) {
targetPivot += pt.tool.originWorld;
break;
}
}
}
if (!m_workspace.reanchorEntityPivot(m_selectedEntityId, m_currentFrame, targetPivot, s)) return;
refreshEditorPage();
refreshDopeSheet();
});
@@ -1344,8 +1379,31 @@ void MainWindow::createProjectTreeDock() {
if (m_selectedEntityId.isEmpty() || !m_editorCanvas) return;
const double s = m_editorCanvas->selectedCombinedScale();
if (s <= 1e-9) return;
QPointF targetCentroid(x, y);
QString parentId;
for (const auto& e : m_workspace.entities()) {
if (e.id == m_selectedEntityId) {
parentId = e.parentId;
break;
}
}
if (!parentId.isEmpty()) {
const auto rf = core::eval::evaluateAtFrame(m_workspace.project(), m_currentFrame, 10);
for (const auto& pe : rf.entities) {
if (pe.entity.id == parentId) {
targetCentroid += pe.entity.originWorld;
break;
}
}
for (const auto& pt : rf.tools) {
if (pt.tool.id == parentId) {
targetCentroid += pt.tool.originWorld;
break;
}
}
}
const bool autoKey = m_chkAutoKeyframe && m_chkAutoKeyframe->isChecked();
if (!m_workspace.moveEntityCentroidTo(m_selectedEntityId, m_currentFrame, QPointF(x, y), s, autoKey)) return;
if (!m_workspace.moveEntityCentroidTo(m_selectedEntityId, m_currentFrame, targetCentroid, s, autoKey)) return;
refreshEditorPage();
refreshDopeSheet();
});
@@ -1410,6 +1468,46 @@ void MainWindow::createProjectTreeDock() {
m_workspace.setToolAlign(m_selectedToolId, a);
refreshEditorPage();
});
connect(m_toolPropertySection, &gui::ToolPropertySection::positionEdited, this, [this](double x, double y) {
if (m_selectedToolId.isEmpty() || !m_workspace.isOpen()) return;
const int f = std::clamp(m_currentFrame, 0, core::Project::kClipFixedFrames - 1);
const auto rf = core::eval::evaluateAtFrame(m_workspace.project(), f, 10);
QPointF currentWorld;
QPointF parentWorld;
QString parentId;
bool found = false;
for (const auto& t : rf.tools) {
if (t.tool.id == m_selectedToolId) {
currentWorld = t.tool.originWorld;
parentId = t.tool.parentId;
found = true;
break;
}
}
if (!found) return;
if (!parentId.isEmpty()) {
for (const auto& e : rf.entities) {
if (e.entity.id == parentId) {
parentWorld = e.entity.originWorld;
break;
}
}
if (qFuzzyIsNull(parentWorld.x()) && qFuzzyIsNull(parentWorld.y())) {
for (const auto& t : rf.tools) {
if (t.tool.id == parentId) {
parentWorld = t.tool.originWorld;
break;
}
}
}
}
const QPointF targetWorld = parentId.isEmpty() ? QPointF(x, y) : (parentWorld + QPointF(x, y));
const QPointF delta = targetWorld - currentWorld;
if (qFuzzyIsNull(delta.x()) && qFuzzyIsNull(delta.y())) return;
if (!m_workspace.moveToolBy(m_selectedToolId, delta, f, true)) return;
refreshEditorPage();
refreshDopeSheet();
});
connect(m_toolPropertySection, &gui::ToolPropertySection::visibleToggled, this, [this](bool on) {
if (m_selectedToolId.isEmpty() || !m_workspace.isOpen()) return;
const int f = std::clamp(m_currentFrame, 0, core::Project::kClipFixedFrames - 1);
@@ -1468,7 +1566,8 @@ void MainWindow::createProjectTreeDock() {
}
void MainWindow::refreshPropertyPanel() {
if (!m_bgPropertySection || !m_entityPropertySection || !m_toolPropertySection || !m_propertyStack) {
if (!m_bgPropertySection || !m_blackholePropertySection || !m_entityPropertySection ||
!m_toolPropertySection || !m_propertyStack) {
return;
}
@@ -1556,6 +1655,15 @@ void MainWindow::refreshPropertyPanel() {
gui::ToolPropertyUiState st;
const int f = std::clamp(m_currentFrame, 0, core::Project::kClipFixedFrames - 1);
const auto* clip = activeClipForUi();
QString parentId;
const auto rf = core::eval::evaluateAtFrame(m_workspace.project(), m_currentFrame, 10);
for (const auto& rt : rf.tools) {
if (rt.tool.id == m_selectedToolId) {
st.position = rt.tool.originWorld;
parentId = rt.tool.parentId;
break;
}
}
for (const auto& t : m_workspace.tools()) {
if (t.id == m_selectedToolId) {
st.displayName = t.displayName.isEmpty() ? t.id : t.displayName;
@@ -1576,12 +1684,59 @@ void MainWindow::refreshPropertyPanel() {
break;
}
}
if (!parentId.isEmpty()) {
QPointF parentWorld;
for (const auto& pe : rf.entities) {
if (pe.entity.id == parentId) {
parentWorld = pe.entity.originWorld;
break;
}
}
if (qFuzzyIsNull(parentWorld.x()) && qFuzzyIsNull(parentWorld.y())) {
for (const auto& pt : rf.tools) {
if (pt.tool.id == parentId) {
parentWorld = pt.tool.originWorld;
break;
}
}
}
st.position -= parentWorld;
st.parentRelativeMode = true;
}
m_toolPropertySection->applyState(st);
m_propertyStack->setCurrentWidget(m_toolPropertySection);
m_dockProperties->setWindowTitle(QStringLiteral("属性 — 工具"));
return;
}
const bool holeUi = m_workspace.isOpen() && !m_selectedBlackholeEntityId.isEmpty();
if (holeUi) {
gui::BlackholePropertyUiState st;
for (const auto& e : m_workspace.entities()) {
if (e.id != m_selectedBlackholeEntityId) {
continue;
}
st.blackholeName = e.blackholeId.isEmpty() ? QStringLiteral("blackhole-%1").arg(e.id) : e.blackholeId;
st.statusText = e.blackholeVisible ? QStringLiteral("") : QStringLiteral("");
if (e.blackholeResolvedBy == QStringLiteral("copy_background")) {
st.methodText = QStringLiteral("复制背景其他区域");
} else if (e.blackholeResolvedBy == QStringLiteral("use_original_background")) {
st.methodText = QStringLiteral("使用原始背景");
} else if (e.blackholeResolvedBy == QStringLiteral("model_inpaint")) {
st.methodText = QStringLiteral("模型补全");
} else if (e.blackholeResolvedBy == QStringLiteral("pending")) {
st.methodText = QStringLiteral("待选择");
} else {
st.methodText = QStringLiteral("未选择");
}
break;
}
m_blackholePropertySection->applyState(st);
m_propertyStack->setCurrentWidget(m_blackholePropertySection);
m_dockProperties->setWindowTitle(QStringLiteral("属性 — 黑洞"));
return;
}
const bool entUi = m_hasSelectedEntity && m_workspace.isOpen() && !m_selectedEntityId.isEmpty() && m_editorCanvas;
if (!entUi) {
m_entityPropertySection->clearDisconnected();
@@ -1595,6 +1750,7 @@ void MainWindow::refreshPropertyPanel() {
double userScale = 1.0;
bool ignoreDist = false;
bool entVisible = true;
QString parentId;
core::EntityIntroContent intro;
const int f = std::clamp(m_currentFrame, 0, core::Project::kClipFixedFrames - 1);
const auto* clip = activeClipForUi();
@@ -1604,6 +1760,7 @@ void MainWindow::refreshPropertyPanel() {
userScale = e.userScale;
intro = e.intro;
ignoreDist = e.ignoreDistanceScale;
parentId = e.parentId;
const QVector<core::Project::ToolKeyframeBool> keys =
(clip && clip->entityVisibilityKeys.contains(e.id))
? clip->entityVisibilityKeys.value(e.id)
@@ -1621,6 +1778,27 @@ void MainWindow::refreshPropertyPanel() {
QStringLiteral("%1自动").arg(m_editorCanvas->selectedDistanceScaleMultiplier(), 0, 'f', 3);
st.pivot = m_editorCanvas->selectedAnimatedOriginWorld();
st.centroid = m_editorCanvas->selectedEntityCentroidWorld();
if (!parentId.isEmpty()) {
QPointF parentWorld;
const auto rf = core::eval::evaluateAtFrame(m_workspace.project(), m_currentFrame, 10);
for (const auto& pe : rf.entities) {
if (pe.entity.id == parentId) {
parentWorld = pe.entity.originWorld;
break;
}
}
if (qFuzzyIsNull(parentWorld.x()) && qFuzzyIsNull(parentWorld.y())) {
for (const auto& pt : rf.tools) {
if (pt.tool.id == parentId) {
parentWorld = pt.tool.originWorld;
break;
}
}
}
st.pivot -= parentWorld;
st.centroid -= parentWorld;
st.parentRelativeMode = true;
}
st.userScale = userScale;
st.ignoreDistanceScale = ignoreDist;
st.visible = entVisible;
@@ -1646,6 +1824,34 @@ void MainWindow::refreshEntityPropertyPanelFast() {
QStringLiteral("%1自动").arg(m_editorCanvas->selectedDistanceScaleMultiplier(), 0, 'f', 3);
st.pivot = m_editorCanvas->selectedAnimatedOriginWorld();
st.centroid = m_editorCanvas->selectedEntityCentroidWorld();
QString parentId;
for (const auto& e : m_workspace.entities()) {
if (e.id == m_selectedEntityId) {
parentId = e.parentId;
break;
}
}
if (!parentId.isEmpty()) {
QPointF parentWorld;
const auto rf = core::eval::evaluateAtFrame(m_workspace.project(), m_currentFrame, 10);
for (const auto& pe : rf.entities) {
if (pe.entity.id == parentId) {
parentWorld = pe.entity.originWorld;
break;
}
}
if (qFuzzyIsNull(parentWorld.x()) && qFuzzyIsNull(parentWorld.y())) {
for (const auto& pt : rf.tools) {
if (pt.tool.id == parentId) {
parentWorld = pt.tool.originWorld;
break;
}
}
}
st.pivot -= parentWorld;
st.centroid -= parentWorld;
st.parentRelativeMode = true;
}
st.userScale = m_editorCanvas->selectedUserScale();
// ignoreDistanceScale 在拖动中不变更fast path 不必更新(避免再遍历 entities
m_entityPropertySection->applyState(st);
@@ -1742,6 +1948,33 @@ void MainWindow::refreshProjectTree() {
});
}
// 黑洞节点:挂在“背景”下,和实体渲染解耦(黑洞可见性独立于实体可见性)
QVector<const core::Project::Entity*> blackholeEnts;
blackholeEnts.reserve(sortedEnts.size());
for (const auto& e : sortedEnts) {
if (!e.cutoutPolygonWorld.isEmpty()) {
blackholeEnts.push_back(&e);
}
}
std::stable_sort(blackholeEnts.begin(), blackholeEnts.end(),
[](const core::Project::Entity* a, const core::Project::Entity* b) {
const QString an = a->displayName.isEmpty() ? a->id : a->displayName;
const QString bn = b->displayName.isEmpty() ? b->id : b->displayName;
return an < bn;
});
for (const auto* e : blackholeEnts) {
auto* it = new QTreeWidgetItem(m_itemBackground);
const QString base = e->displayName.isEmpty() ? e->id : e->displayName;
const QString holeName =
e->blackholeId.isEmpty() ? QStringLiteral("blackhole-%1").arg(e->id) : e->blackholeId;
it->setText(1, QStringLiteral("黑洞 · %1").arg(base));
it->setToolTip(1, QStringLiteral("节点:%1").arg(holeName));
it->setTextAlignment(1, Qt::AlignRight | Qt::AlignVCenter);
it->setData(0, Qt::UserRole, QStringLiteral("blackhole"));
it->setData(0, Qt::UserRole + 1, e->id); // 绑定实体 id便于定位 cutout 多边形
it->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
}
struct NodeRef {
QString kind; // "entity" / "tool"
QString id;
@@ -1845,7 +2078,9 @@ void MainWindow::syncProjectTreeFromCanvasSelection() {
}
m_syncingTreeSelection = true;
m_projectTree->blockSignals(true);
if ((!m_hasSelectedEntity || m_selectedEntityId.isEmpty()) && (!m_hasSelectedTool || m_selectedToolId.isEmpty())) {
if ((!m_hasSelectedEntity || m_selectedEntityId.isEmpty()) &&
(!m_hasSelectedTool || m_selectedToolId.isEmpty()) &&
m_selectedBlackholeEntityId.isEmpty()) {
m_projectTree->clearSelection();
} else {
QTreeWidgetItem* found = nullptr;
@@ -1861,6 +2096,11 @@ void MainWindow::syncProjectTreeFromCanvasSelection() {
found = node;
break;
}
if (!m_selectedBlackholeEntityId.isEmpty() && kind == QStringLiteral("blackhole") &&
id == m_selectedBlackholeEntityId) {
found = node;
break;
}
}
if (found) {
m_projectTree->setCurrentItem(found);
@@ -1882,6 +2122,10 @@ void MainWindow::onProjectTreeItemClicked(QTreeWidgetItem* item, int column) {
if (kind == QStringLiteral("entity")) {
const QString id = item->data(0, Qt::UserRole + 1).toString();
if (!id.isEmpty()) {
m_selectedBlackholeEntityId.clear();
if (m_editorCanvas) {
m_editorCanvas->clearBlackholeSelection();
}
m_hasSelectedTool = false;
m_selectedToolId.clear();
if (m_timeline) {
@@ -1892,6 +2136,10 @@ void MainWindow::onProjectTreeItemClicked(QTreeWidgetItem* item, int column) {
} else if (kind == QStringLiteral("tool")) {
const QString id = item->data(0, Qt::UserRole + 1).toString();
if (!id.isEmpty()) {
m_selectedBlackholeEntityId.clear();
if (m_editorCanvas) {
m_editorCanvas->clearBlackholeSelection();
}
m_hasSelectedTool = true;
m_selectedToolId = id;
m_hasSelectedEntity = false;
@@ -1904,10 +2152,28 @@ void MainWindow::onProjectTreeItemClicked(QTreeWidgetItem* item, int column) {
}
refreshPropertyPanel();
}
} else if (kind == QStringLiteral("blackhole")) {
const QString entityId = item->data(0, Qt::UserRole + 1).toString();
if (!entityId.isEmpty()) {
m_selectedBlackholeEntityId = entityId;
m_hasSelectedTool = false;
m_selectedToolId.clear();
m_hasSelectedEntity = false;
m_selectedEntityId.clear();
m_selectedEntityDisplayNameCache.clear();
if (m_editorCanvas) {
m_editorCanvas->clearEntitySelection();
m_editorCanvas->selectBlackholeByEntityId(entityId);
}
updateTimelineTracks();
refreshPropertyPanel();
}
} else if (kind == QStringLiteral("background")) {
m_selectedBlackholeEntityId.clear();
m_hasSelectedTool = false;
m_selectedToolId.clear();
m_editorCanvas->clearEntitySelection();
m_editorCanvas->clearBlackholeSelection();
updateTimelineTracks();
}
}
@@ -2394,6 +2660,7 @@ void MainWindow::rebuildCentralPages() {
connect(m_editorCanvas, &EditorCanvas::selectedEntityChanged, this, [this](bool hasSel, const QString& id, int depth, const QPointF& origin) {
m_hasSelectedEntity = hasSel;
m_selectedEntityId = id;
m_selectedBlackholeEntityId.clear();
m_selectedEntityDepth = depth;
m_selectedEntityOrigin = origin;
m_hasSelectedTool = false;
@@ -2418,6 +2685,7 @@ void MainWindow::rebuildCentralPages() {
Q_UNUSED(origin);
m_hasSelectedTool = hasSel;
m_selectedToolId = id;
m_selectedBlackholeEntityId.clear();
if (hasSel) {
m_hasSelectedEntity = false;
m_selectedEntityId.clear();
@@ -2475,6 +2743,11 @@ void MainWindow::rebuildCentralPages() {
}
}
}
ent.blackholeVisible = true;
if (ent.blackholeId.isEmpty() && !ent.id.isEmpty()) {
ent.blackholeId = QStringLiteral("blackhole-%1").arg(ent.id);
}
ent.blackholeResolvedBy = QStringLiteral("pending");
if (!m_workspace.addEntity(ent, img)) {
QMessageBox::warning(this, QStringLiteral("实体"), QStringLiteral("保存实体失败。"));
return;
@@ -2768,6 +3041,11 @@ void MainWindow::rebuildCentralPages() {
if (ent.displayName.isEmpty()) {
// 允许空:界面会用 id 展示
}
ent.blackholeVisible = true;
if (ent.blackholeId.isEmpty() && !ent.id.isEmpty()) {
ent.blackholeId = QStringLiteral("blackhole-%1").arg(ent.id);
}
ent.blackholeResolvedBy = QStringLiteral("pending");
QImage bg(m_workspace.backgroundAbsolutePath());
if (!bg.isNull() && bg.format() != QImage::Format_ARGB32_Premultiplied) {
@@ -2812,6 +3090,26 @@ void MainWindow::rebuildCentralPages() {
refreshProjectTree();
updateUiEnabledState();
});
connect(m_editorCanvas, &EditorCanvas::requestResolveBlackholeCopy, this,
[this](const QString& entityId, const QPoint& sourceOffsetPx) {
if (!m_workspace.resolveBlackholeByCopyBackground(entityId, sourceOffsetPx, true)) {
QMessageBox::warning(
this,
QStringLiteral("黑洞修复"),
QStringLiteral("复制背景区域失败。请重新拖动取样框,确保采样区域在背景范围内。"));
return;
}
statusBar()->showMessage(QStringLiteral("黑洞已通过背景复制修复"));
refreshProjectTree();
updateUiEnabledState();
if (m_editorCanvas) {
m_editorCanvas->notifyBackgroundContentChanged();
}
refreshEditorPage();
if (m_previewRequested) {
refreshPreviewPage();
}
});
connect(m_editorCanvas, &EditorCanvas::presentationEntityIntroRequested, this,
[this](const QString& id, QPointF anchorView) {
@@ -3194,7 +3492,8 @@ void MainWindow::refreshDopeSheet() {
break;
}
}
addChannel(QStringLiteral("位置"), 0, hasLoc);
const QString locLabel = e.parentId.isEmpty() ? QStringLiteral("位置") : QStringLiteral("相对位置");
addChannel(locLabel, 0, hasLoc);
addChannel(QStringLiteral("缩放"), 1, hasSc);
addChannel(QStringLiteral("图像"), 2, hasIm);
}
@@ -3221,6 +3520,70 @@ void MainWindow::showBackgroundContextMenu(const QPoint& globalPos) {
refreshPreviewPage();
}
void MainWindow::showBlackholeContextMenu(const QPoint& globalPos, const QString& entityId) {
if (entityId.isEmpty() || !m_workspace.isOpen()) {
return;
}
m_selectedBlackholeEntityId = entityId;
if (m_editorCanvas) {
m_editorCanvas->selectBlackholeByEntityId(entityId);
}
syncProjectTreeFromCanvasSelection();
QString holeLabel = entityId;
for (const auto& e : m_workspace.entities()) {
if (e.id == entityId) {
if (!e.blackholeId.isEmpty()) {
holeLabel = e.blackholeId;
} else {
holeLabel = QStringLiteral("blackhole-%1").arg(entityId);
}
break;
}
}
QMenu menu(this);
QAction* actResolve = menu.addAction(QStringLiteral("修复"));
QAction* chosen = menu.exec(globalPos);
if (!chosen || chosen != actResolve) {
return;
}
BlackholeResolveDialog dlg(holeLabel, this);
if (dlg.exec() != QDialog::Accepted) {
return;
}
bool ok = false;
if (dlg.selectedAlgorithm() == BlackholeResolveDialog::Algorithm::CopyBackgroundRegion) {
if (!m_editorCanvas || !m_editorCanvas->startBlackholeCopyResolve(entityId)) {
QMessageBox::warning(
this,
QStringLiteral("黑洞修复"),
QStringLiteral("无法进入画布拖动模式,请确认黑洞与背景数据有效。"));
return;
}
statusBar()->showMessage(QStringLiteral("拖动画布中的青色取样框松开鼠标即应用Esc 取消"));
return;
} else {
ok = m_workspace.resolveBlackholeByUseOriginalBackground(entityId);
if (!ok) {
QMessageBox::warning(this, QStringLiteral("黑洞修复"), QStringLiteral("应用“使用原始背景”失败。"));
}
}
if (ok) {
statusBar()->showMessage(QStringLiteral("黑洞修复已应用"));
refreshProjectTree();
updateUiEnabledState();
refreshEditorPage();
if (m_previewRequested) {
refreshPreviewPage();
}
}
}
void MainWindow::onNewProject() {
if (m_workspace.isOpen()) {