This commit is contained in:
2026-05-23 20:49:32 +08:00
parent 12ea7273ef
commit f78de517ec
5 changed files with 245 additions and 310 deletions
+145 -276
View File
@@ -43,6 +43,7 @@
#include <QDialog>
#include <QDockWidget>
#include <QDrag>
#include <QElapsedTimer>
#include <QEvent>
#include <QMouseEvent>
#include <QFrame>
@@ -307,9 +308,7 @@ public:
EditorCanvas* canvas = nullptr;
QWidget* modeDock = nullptr;
QWidget* toolDock = nullptr;
QWidget* previewPlaybackBar = nullptr;
QWidget* previewArrowLeft = nullptr;
QWidget* previewArrowRight = nullptr;
QWidget* previewBackButton = nullptr;
void relayoutFloaters() {
if (canvas) {
@@ -333,6 +332,12 @@ public:
modeDock->adjustSize();
}
if (previewBackButton && previewBackButton->isVisible()) {
previewBackButton->updateGeometry();
previewBackButton->adjustSize();
previewBackButton->move(kMargin, kMargin);
}
if (toolDock && toolDock->isVisible()) {
if (QLayout* lay = toolDock->layout()) {
lay->activate();
@@ -343,6 +348,9 @@ public:
if (modeDock && modeDock->isVisible()) {
y = modeDock->y() + modeDock->height() + kGap;
}
if (previewBackButton && previewBackButton->isVisible()) {
y = std::max(y, previewBackButton->y() + previewBackButton->height() + kGap);
}
toolDock->move(kMargin, y);
}
@@ -353,36 +361,8 @@ public:
if (toolDock && toolDock->isVisible()) {
toolDock->raise();
}
constexpr int arrowW = 44;
constexpr int arrowH = 72;
auto placeArrowCenterY = [&](QWidget* arrow, bool leftSide) {
if (!arrow || !arrow->isVisible()) {
return;
}
arrow->setFixedSize(arrowW, arrowH);
const int y = std::max(kMargin, (height() - arrowH) / 2);
if (leftSide) {
arrow->move(kMargin, y);
} else {
arrow->move(std::max(kMargin, width() - arrowW - kMargin), y);
}
arrow->raise();
};
placeArrowCenterY(previewArrowLeft, true);
placeArrowCenterY(previewArrowRight, false);
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();
if (previewBackButton && previewBackButton->isVisible()) {
previewBackButton->raise();
}
}
@@ -752,71 +732,35 @@ void MainWindow::createTimelineDock() {
});
m_playTimer = new QTimer(this);
m_playTimer->setTimerType(Qt::PreciseTimer);
connect(m_playTimer, &QTimer::timeout, this, [this]() {
if (!m_timeline || !m_workspace.isOpen() || m_previewRequested) {
return;
}
const int len = m_workspace.activeAnimationLengthFrames();
m_currentFrame = (m_currentFrame + 1) % std::max(1, len);
m_timeline->setCurrentFrame(m_currentFrame);
m_timeline->setCurrentFrameProgrammatic(m_currentFrame);
applyTimelineFrameToEditor(m_currentFrame);
});
connect(m_btnPlay, &QToolButton::toggled, this, &MainWindow::onTogglePlay);
connect(m_timeline, &TimelineWidget::frameScrubbed, this, [this](int v) {
// 轻量实时预览:只更新画布帧,不做 refreshEditorPage 的全量重建
m_currentFrame = std::clamp(v, 0, m_workspace.activeAnimationLengthFrames() - 1);
if (m_editorCanvas && m_workspace.isOpen()) {
// 需要重新求值实体几何/贴图轨道,否则拖动实体与属性变更在非 0 帧会失效
m_timelineScrubbing = true;
m_editorCanvas->setCurrentFrame(m_currentFrame);
const core::eval::ResolvedProjectFrame rf = core::eval::evaluateAtFrame(m_workspace.project(), m_currentFrame, 10);
QVector<core::Project::Entity> ents;
QVector<double> entOps;
ents.reserve(rf.entities.size());
entOps.reserve(rf.entities.size());
for (const auto& re : rf.entities) {
ents.push_back(re.entity);
entOps.push_back(re.opacity);
}
m_editorCanvas->setEntities(ents, entOps, m_workspace.projectDir());
QVector<core::Project::Tool> tools;
QVector<double> toolOps;
tools.reserve(rf.tools.size());
toolOps.reserve(rf.tools.size());
for (const auto& rt : rf.tools) {
tools.push_back(rt.tool);
toolOps.push_back(rt.opacity);
}
m_editorCanvas->setTools(tools, toolOps);
QVector<core::Project::Camera> cams;
cams.reserve(rf.cameras.size());
for (const auto& rc : rf.cameras) {
cams.push_back(rc.camera);
}
m_editorCanvas->setCameraOverlays(cams, m_selectedCameraId, m_tempHiddenCameraIds);
m_editorCanvas->setTempHiddenIds(m_tempHiddenEntityIds, m_tempHiddenToolIds);
const bool presentation = m_previewRequested && m_workspace.hasBackground();
m_editorCanvas->setPreviewCameraViewLocked(false);
if (presentation) {
const QString acid = m_workspace.project().activeCameraId();
if (!acid.isEmpty()) {
for (const auto& rc : rf.cameras) {
if (rc.camera.id == acid) {
m_editorCanvas->setPreviewCameraViewLocked(true);
m_editorCanvas->applyCameraViewport(rc.camera.centerWorld, rc.camera.viewScale);
break;
}
}
}
}
m_timelineScrubbing = false;
} else if (m_editorCanvas) {
m_editorCanvas->setCurrentFrame(m_currentFrame);
const int len = m_workspace.activeAnimationLengthFrames();
const int maxF = std::max(0, len - 1);
m_currentFrame = std::clamp(v, 0, maxF);
if (!m_timelineEditorApplyThrottle.isValid()) {
m_timelineEditorApplyThrottle.start();
applyTimelineFrameToEditor(m_currentFrame);
return;
}
if (m_timelineEditorApplyThrottle.hasExpired(22)) {
applyTimelineFrameToEditor(m_currentFrame);
m_timelineEditorApplyThrottle.restart();
}
});
connect(m_timeline, &TimelineWidget::frameCommitted, this, [this](int v) {
// 松手再做一次较重刷新(如果后续还有需要同步的 UI)
m_currentFrame = std::clamp(v, 0, m_workspace.activeAnimationLengthFrames() - 1);
m_timelineEditorApplyThrottle.invalidate();
m_currentFrame = std::clamp(v, 0, std::max(0, m_workspace.activeAnimationLengthFrames() - 1));
refreshEditorPage();
});
connect(btnKeyCombined, &QToolButton::clicked, this, &MainWindow::onInsertCombinedKey);
@@ -1163,7 +1107,7 @@ void MainWindow::onTogglePlay(bool on) {
if (m_playTimer) {
if (on) {
const int fps = m_workspace.isOpen() ? m_workspace.activeAnimationFps() : 30;
m_playTimer->start(1000 / std::max(1, fps));
m_playTimer->start(std::max(1, qRound(1000.0 / double(std::max(1, fps)))));
} else {
m_playTimer->stop();
}
@@ -1203,19 +1147,11 @@ void MainWindow::startPreviewPlayback() {
const QString id = effectivePreviewAnimationId();
const int fps = m_workspace.animationFpsForId(id);
if (m_previewPlayTimer) {
m_previewPlayTimer->start(1000 / std::max(1, fps));
m_previewPlayTimer->start(std::max(1, qRound(1000.0 / double(std::max(1, fps)))));
}
syncPreviewPlaybackBar();
}
void MainWindow::togglePreviewPlayback() {
if (m_previewPlaying) {
stopPreviewPlayback();
} else {
startPreviewPlayback();
}
}
QVector<QString> MainWindow::previewAnimationSchemeIdsNonNone() const {
QVector<QString> out;
if (!m_workspace.isOpen()) {
@@ -1252,44 +1188,32 @@ QString MainWindow::effectivePreviewAnimationId() const {
return fb.isEmpty() ? QStringLiteral("none") : fb;
}
void MainWindow::refreshPreviewSchemeCombo() {
if (!m_previewSchemeCombo || !m_workspace.isOpen()) {
void MainWindow::syncPreviewFloatingChrome() {
if (!m_canvasHost || !m_floatingModeDock || !m_previewBackToStaticBtn) {
return;
}
const QString keep = effectivePreviewAnimationId();
m_previewSchemeCombo->blockSignals(true);
m_previewSchemeCombo->clear();
QString noneLabel = QStringLiteral("画布");
for (const auto& a : m_workspace.project().animations()) {
if (a.id == QStringLiteral("none")) {
if (!a.name.isEmpty())
noneLabel = a.name;
break;
}
auto* ch = static_cast<CanvasHost*>(m_canvasHost);
ch->previewBackButton = m_previewBackToStaticBtn;
const bool projectOpen = m_workspace.isOpen();
if (!projectOpen) {
m_floatingModeDock->hide();
m_previewBackToStaticBtn->hide();
ch->relayoutFloaters();
return;
}
if (m_workspace.project().findAnimationById(QStringLiteral("none"))) {
m_previewSchemeCombo->addItem(noneLabel, QStringLiteral("none"));
const bool preview = m_previewRequested;
const bool showBack = preview && (effectivePreviewAnimationId() != QStringLiteral("none"));
if (showBack) {
m_floatingModeDock->hide();
m_previewBackToStaticBtn->show();
} else {
m_floatingModeDock->show();
m_previewBackToStaticBtn->hide();
}
for (const auto& a : m_workspace.project().animations()) {
if (a.id == QStringLiteral("none")) {
continue;
}
const QString label = a.name.isEmpty() ? a.id : a.name;
m_previewSchemeCombo->addItem(label, a.id);
}
int idx = -1;
for (int i = 0; i < m_previewSchemeCombo->count(); ++i) {
if (m_previewSchemeCombo->itemData(i).toString() == keep) {
idx = i;
break;
}
}
if (idx >= 0) {
m_previewSchemeCombo->setCurrentIndex(idx);
} else if (m_previewSchemeCombo->count() > 0) {
m_previewSchemeCombo->setCurrentIndex(0);
}
m_previewSchemeCombo->blockSignals(false);
ch->relayoutFloaters();
}
void MainWindow::requestPreviewSchemeSwitchTo(const QString& id, bool showProgress) {
@@ -1318,7 +1242,6 @@ void MainWindow::requestPreviewSchemeSwitchTo(const QString& id, bool showProgre
m_previewFrame = 0;
auto finishUi = [this]() {
refreshPreviewSchemeCombo();
refreshEditorPage();
if (effectivePreviewAnimationId() != QStringLiteral("none"))
startPreviewPlayback();
@@ -1338,35 +1261,6 @@ void MainWindow::requestPreviewSchemeSwitchTo(const QString& id, bool showProgre
finishUi();
}
void MainWindow::switchPreviewSchemeAdjacent(int delta) {
QVector<QString> ids = previewAnimationSchemeIdsNonNone();
if (ids.size() <= 1) {
return;
}
const QString cur = effectivePreviewAnimationId();
int ix = 0;
for (int i = 0; i < ids.size(); ++i) {
if (ids[i] == cur) {
ix = i;
break;
}
}
const int n = static_cast<int>(ids.size());
ix = ((ix + delta) % n + n) % n;
requestPreviewSchemeSwitchTo(ids[ix], true);
}
void MainWindow::onPreviewSchemeComboChanged(int idx) {
if (!m_previewSchemeCombo || !m_workspace.isOpen() || !m_previewRequested || idx < 0) {
return;
}
const QString animId = m_previewSchemeCombo->itemData(idx).toString();
if (animId.isEmpty() || animId == m_previewAnimationId) {
return;
}
requestPreviewSchemeSwitchTo(animId, true);
}
void MainWindow::syncHotspotEnterPreviewCombo() {
const bool any = previewAnimationSchemeIdsNonNone().size() > 0;
if (m_btnHotspotEnterPreview) {
@@ -1401,10 +1295,7 @@ void MainWindow::syncEditorRailForMode() {
}
void MainWindow::syncPreviewPlaybackBar() {
if (!m_previewBtnPlay) {
return;
}
m_previewBtnPlay->setText(m_previewPlaying ? QStringLiteral("暂停") : QStringLiteral("播放"));
syncPreviewFloatingChrome();
}
void MainWindow::onInsertCombinedKey() {
@@ -1474,7 +1365,7 @@ void MainWindow::createAnimationMenu() {
AnimationSchemeSettingsDialog dlg(AnimationSchemeSettingsDialog::Mode::Edit, m_workspace, this);
dlg.setEditTarget(targetId);
if (dlg.exec() == QDialog::Accepted) {
refreshPreviewSchemeCombo();
syncPreviewFloatingChrome();
refreshEditorPage();
}
});
@@ -3031,10 +2922,6 @@ void MainWindow::updateUiEnabledState() {
m_modeSelector->setItemData(3, (projectOpen && hasBg) ? QVariant() : QVariant(0), Qt::UserRole - 1);
m_modeSelector->blockSignals(false);
}
if (m_previewBackToCanvasBtn) {
m_previewBackToCanvasBtn->setVisible(m_previewRequested && projectOpen &&
effectivePreviewAnimationId() != QStringLiteral("none"));
}
if (projectOpen && hasBg) {
syncHotspotEnterPreviewCombo();
}
@@ -3144,24 +3031,11 @@ void MainWindow::applyUiMode(UiMode mode) {
}
}
if (m_floatingModeDock) {
m_floatingModeDock->setVisible(projectOpen);
}
if (m_floatingToolDock) {
m_floatingToolDock->setVisible(projectOpen && !preview);
}
syncPreviewFloatingChrome();
syncEditorRailForMode();
if (m_previewPlaybackBar) {
m_previewPlaybackBar->setVisible(projectOpen && preview);
}
if (!preview || !projectOpen) {
if (m_previewArrowLeft) {
m_previewArrowLeft->hide();
}
if (m_previewArrowRight) {
m_previewArrowRight->hide();
}
}
if (m_canvasHost) {
m_canvasHost->updateGeometry();
static_cast<CanvasHost*>(m_canvasHost)->relayoutFloaters();
@@ -3372,47 +3246,25 @@ void MainWindow::rebuildCentralPages() {
m_entityIntroPopup = new gui::EntityIntroPopup(this);
m_previewArrowLeft = new QToolButton(canvasHost);
m_previewArrowLeft->setText(QStringLiteral(""));
m_previewArrowLeft->setToolTip({});
polishCompactToolButton(m_previewArrowLeft, 44);
m_previewArrowLeft->setStyleSheet(
QStringLiteral("QToolButton { background-color: rgba(40,40,40,168); border-radius: 8px; color: palette(text); "
"padding: 4px 8px; } QToolButton:hover { background-color: rgba(60,60,60,208); }"));
m_previewArrowLeft->hide();
connect(m_previewArrowLeft, &QToolButton::clicked, this,
[this]() { switchPreviewSchemeAdjacent(-1); });
m_previewArrowRight = new QToolButton(canvasHost);
m_previewArrowRight->setText(QStringLiteral(""));
m_previewArrowRight->setToolTip({});
polishCompactToolButton(m_previewArrowRight, 44);
m_previewArrowRight->setStyleSheet(
QStringLiteral("QToolButton { background-color: rgba(40,40,40,168); border-radius: 8px; color: palette(text); "
"padding: 4px 8px; } QToolButton:hover { background-color: rgba(60,60,60,208); }"));
m_previewArrowRight->hide();
connect(m_previewArrowRight, &QToolButton::clicked, this,
[this]() { switchPreviewSchemeAdjacent(1); });
canvasHost->previewArrowLeft = m_previewArrowLeft;
canvasHost->previewArrowRight = m_previewArrowRight;
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_previewSchemeCombo = new QComboBox(m_previewPlaybackBar);
m_previewSchemeCombo->setMinimumWidth(160);
pbl->addWidget(m_previewSchemeCombo);
m_previewBtnPlay = new QToolButton(m_previewPlaybackBar);
m_previewBtnPlay->setCheckable(false);
m_previewBtnPlay->setText(QStringLiteral("播放"));
m_previewBtnPlay->setToolTip({});
polishCompactToolButton(m_previewBtnPlay, 36);
pbl->addWidget(m_previewBtnPlay);
m_previewPlaybackBar->setParent(canvasHost);
m_previewBackToStaticBtn = new QToolButton(canvasHost);
m_previewBackToStaticBtn->setText(QStringLiteral("返回"));
m_previewBackToStaticBtn->setToolTip(QStringLiteral("回到无动画的静态预览(停止播放),并恢复左上角模式切换"));
m_previewBackToStaticBtn->setStyleSheet(
QStringLiteral("QToolButton { background-color: palette(base); border: 1px solid palette(midlight); "
"border-radius: 6px; padding: 4px 10px; }"
"QToolButton:hover { background-color: palette(light); }"));
polishCompactToolButton(m_previewBackToStaticBtn, 36);
m_previewBackToStaticBtn->hide();
canvasHost->previewBackButton = m_previewBackToStaticBtn;
connect(m_previewBackToStaticBtn, &QToolButton::clicked, this, [this]() {
if (!m_previewRequested) {
return;
}
stopPreviewPlayback();
m_previewAnimationId = QStringLiteral("none");
m_previewFrame = 0;
refreshEditorPage();
});
m_floatingModeDock = new QFrame(canvasHost);
m_floatingModeDock->setObjectName(QStringLiteral("FloatingModeDock"));
@@ -3589,7 +3441,7 @@ void MainWindow::rebuildCentralPages() {
m_btnHotspotEnterPreview = new QToolButton(m_floatingToolDock);
m_btnHotspotEnterPreview->setText(QStringLiteral("预览"));
m_btnHotspotEnterPreview->setVisible(false);
m_btnHotspotEnterPreview->setToolTip({});
m_btnHotspotEnterPreview->setToolTip(QStringLiteral("进入无动画的静态预览;点击画布上的热点再播放方案动画"));
polishCompactToolButton(m_btnHotspotEnterPreview, 30);
toolLayout->addWidget(m_btnHotspotEnterPreview, 0, Qt::AlignHCenter);
@@ -4231,13 +4083,12 @@ void MainWindow::rebuildCentralPages() {
}
});
connect(m_btnHotspotEnterPreview, &QToolButton::clicked, this, [this]() {
const QString aid = firstAnimationSchemeIdNonNoneWs(m_workspace.project());
if (aid.isEmpty()) {
if (previewAnimationSchemeIdsNonNone().isEmpty()) {
return;
}
m_animationRequested = false;
m_hotspotRequested = false;
m_previewAnimationId = aid;
m_previewAnimationId = QStringLiteral("none");
m_previewFrame = 0;
setPreviewRequested(true);
if (m_modeSelector) {
@@ -4245,7 +4096,11 @@ void MainWindow::rebuildCentralPages() {
m_modeSelector->setCurrentIndex(3);
m_modeSelector->blockSignals(false);
}
requestPreviewSchemeSwitchTo(aid, true);
stopPreviewPlayback();
syncPreviewFloatingChrome();
refreshEditorPage();
updateUiEnabledState();
refreshProjectTree();
});
m_centerStack->addWidget(m_pageWelcome);
@@ -4437,9 +4292,6 @@ void MainWindow::refreshEditorPage() {
applyTimelineFromProject();
core::eval::ResolvedProjectFrame rf;
const bool presentationPreview = presentation;
if (presentationPreview && !m_previewPlaying) {
refreshPreviewSchemeCombo();
}
if (presentationPreview) {
const QString pvId = effectivePreviewAnimationId();
const int pvLen = m_workspace.animationLengthFramesForId(pvId);
@@ -4484,7 +4336,9 @@ void MainWindow::refreshEditorPage() {
m_editorCanvas->setPreviewCameraViewLocked(false);
m_editorCanvas->clearCameraSelection();
} else {
m_editorCanvas->setCameraOverlays(cams, m_selectedCameraId, m_tempHiddenCameraIds);
if (!m_editorCanvas->isDraggingCamera()) {
m_editorCanvas->setCameraOverlays(cams, m_selectedCameraId, m_tempHiddenCameraIds);
}
m_editorCanvas->setPreviewCameraViewLocked(false);
if (presentation) {
const QString acid = m_workspace.project().activeCameraId();
@@ -4520,6 +4374,7 @@ void MainWindow::refreshEditorPage() {
}
}
}
syncPreviewFloatingChrome();
refreshPropertyPanel();
if (m_canvasHost) {
m_canvasHost->updateGeometry();
@@ -4621,6 +4476,61 @@ void MainWindow::updateTimelineTracks() {
}
}
void MainWindow::applyTimelineFrameToEditor(int frame) {
if (!m_editorCanvas || !m_workspace.isOpen()) {
return;
}
m_editorCanvas->setSuppressSelectionSignals(true);
const int len = m_workspace.activeAnimationLengthFrames();
const int f = std::clamp(frame, 0, std::max(0, len - 1));
m_timelineScrubbing = true;
m_editorCanvas->setCurrentFrame(f);
const core::eval::ResolvedProjectFrame rf = core::eval::evaluateAtFrame(m_workspace.project(), f, 10);
QVector<core::Project::Entity> ents;
QVector<double> entOps;
ents.reserve(rf.entities.size());
entOps.reserve(rf.entities.size());
for (const auto& re : rf.entities) {
ents.push_back(re.entity);
entOps.push_back(re.opacity);
}
m_editorCanvas->setEntities(ents, entOps, m_workspace.projectDir());
QVector<core::Project::Tool> tools;
QVector<double> toolOps;
tools.reserve(rf.tools.size());
toolOps.reserve(rf.tools.size());
for (const auto& rt : rf.tools) {
tools.push_back(rt.tool);
toolOps.push_back(rt.opacity);
}
m_editorCanvas->setTools(tools, toolOps);
QVector<core::Project::Camera> cams;
cams.reserve(rf.cameras.size());
for (const auto& rc : rf.cameras) {
cams.push_back(rc.camera);
}
if (!m_editorCanvas->isDraggingCamera()) {
m_editorCanvas->setCameraOverlays(cams, m_selectedCameraId, m_tempHiddenCameraIds);
}
m_editorCanvas->setTempHiddenIds(m_tempHiddenEntityIds, m_tempHiddenToolIds);
const bool presentation = m_previewRequested && m_workspace.hasBackground();
m_editorCanvas->setPreviewCameraViewLocked(false);
if (presentation) {
const QString acid = m_workspace.project().activeCameraId();
if (!acid.isEmpty()) {
for (const auto& rc : rf.cameras) {
if (rc.camera.id == acid) {
m_editorCanvas->setPreviewCameraViewLocked(true);
m_editorCanvas->applyCameraViewport(rc.camera.centerWorld, rc.camera.viewScale);
break;
}
}
}
}
m_timelineScrubbing = false;
m_editorCanvas->setSuppressSelectionSignals(false);
}
void MainWindow::applyTimelineFromProject() {
if (!m_timeline || !m_workspace.isOpen()) {
return;
@@ -4629,7 +4539,9 @@ void MainWindow::applyTimelineFromProject() {
const int local = std::clamp(g, 0, m_workspace.activeAnimationLengthFrames() - 1);
m_timeline->setFrameRange(0, m_workspace.activeAnimationLengthFrames());
m_timeline->setCurrentFrameProgrammatic(local);
if (m_editorCanvas) m_editorCanvas->setCurrentFrame(g);
if (m_editorCanvas) {
m_editorCanvas->setCurrentFrame(local);
}
}
void MainWindow::refreshDopeSheet() {
@@ -5413,50 +5325,7 @@ void MainWindow::onAbout() {
aboutDialog->exec();
}
void MainWindow::updatePreviewEdgeArrowsHover(const QPointF& canvasHostPos) {
if (!m_previewArrowLeft || !m_previewArrowRight || !m_canvasHost || !m_previewRequested) {
return;
}
if (previewAnimationSchemeIdsNonNone().size() <= 1) {
m_previewArrowLeft->hide();
m_previewArrowRight->hide();
return;
}
constexpr qreal kZone = 96.0;
const qreal w = m_canvasHost->width();
const bool nearLeft = canvasHostPos.x() < kZone;
const bool nearRight = canvasHostPos.x() > w - kZone;
m_previewArrowLeft->setVisible(m_previewRequested && nearLeft);
m_previewArrowRight->setVisible(m_previewRequested && nearRight);
if (auto* ch = dynamic_cast<CanvasHost*>(m_canvasHost)) {
ch->relayoutFloaters();
}
}
bool MainWindow::eventFilter(QObject* watched, QEvent* event) {
if (watched == m_editorCanvas && m_previewRequested && m_editorCanvas && m_canvasHost) {
if (event->type() == QEvent::MouseMove) {
const auto me = static_cast<QMouseEvent*>(event);
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
const QPoint pe = me->position().toPoint();
#else
const QPoint pe = me->pos();
#endif
const QPoint inHost = m_editorCanvas->mapTo(m_canvasHost, pe);
updatePreviewEdgeArrowsHover(QPointF(inHost));
} else if (event->type() == QEvent::Leave) {
if (m_previewArrowLeft) {
m_previewArrowLeft->hide();
}
if (m_previewArrowRight) {
m_previewArrowRight->hide();
}
if (auto* ch = dynamic_cast<CanvasHost*>(m_canvasHost)) {
ch->relayoutFloaters();
}
}
}
if (event->type() == QEvent::Resize && watched == m_dockProjectTree) {
if (m_dockProjectTree && m_workspace.isOpen() && !m_previewRequested && !m_dockProjectTree->isFloating()) {
const int w = m_dockProjectTree->width();