#pragma once #include "core/workspace/ProjectWorkspace.h" #include "main_window/RecentProjectHistory.h" #include #include #include #include #include #include #include #include class ToolOptionPopup; class QAction; class QCheckBox; class QComboBox; class QDockWidget; class QFormLayout; class QLabel; class QMenu; class QFrame; class QIcon; class QPushButton; class QListWidget; class QSlider; class QStackedWidget; class QToolButton; class QTreeWidget; class QTreeWidgetItem; class QWidget; class EditorCanvas; class TimelineWidget; class TimelineEditorModel; namespace gui { class BackgroundPropertySection; class BlackholePropertySection; class EntityPropertySection; class ToolPropertySection; class CameraPropertySection; class HotspotPropertySection; class EntityIntroPopup; class ResourceLibraryDock; } namespace core::library { class ResourceLibraryProvider; class FakeResourceLibraryProvider; class OnlineResourceLibraryProvider; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget* parent = nullptr); protected: bool eventFilter(QObject* watched, QEvent* event) override; private slots: // 文件菜单槽函数 void onNewProject(); void onOpenProject(); void onSaveProject(); void onCloseProject(); // 编辑菜单槽函数 void onUndo(); void onRedo(); void onCopyObject(); void onPasteObject(); // 帮助菜单槽函数 void onAbout(); void onComputeDepth(); void onTogglePlay(bool on); void onInsertCombinedKey(); // 位置 + userScale void onProjectTreeItemClicked(QTreeWidgetItem* item, int column); private: void computeDepthAsync(); // UI 状态分三种: // - Welcome:未打开项目。只显示欢迎页,其它 dock 一律隐藏,视图开关禁用。 // - EntityEditor:静态实体编辑(activeAnimationId=none,不显示摄像机)。 // - AnimationEditor:动画编辑(真实动画方案,可编辑摄像机/时间轴)。 // - HotspotEditor:展示热点布点(要求:项目已打开且背景不为空)。 // - Preview:预览展示。用于全流程完成后的展示(要求:项目已打开且背景不为空)。 enum class UiMode { Welcome, EntityEditor, AnimationEditor, HotspotEditor, Preview }; void createMenus(); // 菜单和工具栏 void createFileMenu(); // 文件菜单 void createEditMenu(); // 编辑菜单 void createHelpMenu(); // 帮助菜单 void createViewMenu(); // 视图菜单 void createAnimationMenu(); void createWindowMenu(); // 窗口菜单(资源库等) void createProjectTreeDock(); void createTimelineDock(); void createResourceLibraryDock(); void refreshProjectTree(); void updateUiEnabledState(); // 更新“可用性/勾选/默认显隐”,不要做业务逻辑 void applyUiMode(UiMode mode); // 统一控制 welcome/editor 两态的显隐策略 UiMode currentUiMode() const; // 根据 workspace 状态推导 void syncCanvasViewMenuFromState(); void showProjectRootContextMenu(const QPoint& globalPos); void showBackgroundContextMenu(const QPoint& globalPos); void showBlackholeContextMenu(const QPoint& globalPos, const QString& entityId); void rebuildCentralPages(); void showWelcomePage(); void showEditorPage(); void showPreviewPage(); void refreshWelcomeRecentList(); void openProjectFromPath(const QString& dir); /// 打开/新建工程后:模态「加载中」直至首张背景可显示(无背景则立即继续) void presentWorkspaceEditorWithBlockingInitialLoad(); void refreshPreviewPage(); void refreshEditorPage(); void applyTimelineFromProject(); /// 时间轴当前帧 → 画布:求值并回灌实体/工具/摄像机(不经 TimelineWidget::setCurrentFrame,避免触发 frameScrubbed) void applyTimelineFrameToEditor(int frame); void updateTimelineTracks(); void refreshDopeSheet(); void setPreviewRequested(bool preview); void syncPreviewPlaybackBar(); void syncPreviewFloatingChrome(); void stopEditorPlayback(); void stopPreviewPlayback(); void startPreviewPlayback(); QString effectivePreviewAnimationId() const; void requestPreviewSchemeSwitchTo(const QString& id, bool warmupDialog); QVector previewAnimationSchemeIdsNonNone() const; void syncHotspotEnterPreviewCombo(); void syncEditorRailForMode(); QStackedWidget* m_centerStack = nullptr; QWidget* m_pageWelcome = nullptr; QTreeWidget* m_welcomeRecentTree = nullptr; QLabel* m_welcomeRecentEmptyLabel = nullptr; QWidget* m_pageEditor = nullptr; QWidget* m_canvasHost = nullptr; QFrame* m_floatingModeDock = nullptr; QFrame* m_floatingToolDock = nullptr; QComboBox* m_modeSelector = nullptr; QStackedWidget* m_propertyStack = nullptr; gui::BackgroundPropertySection* m_bgPropertySection = nullptr; gui::BlackholePropertySection* m_blackholePropertySection = nullptr; gui::EntityPropertySection* m_entityPropertySection = nullptr; gui::ToolPropertySection* m_toolPropertySection = nullptr; gui::CameraPropertySection* m_cameraPropertySection = nullptr; gui::HotspotPropertySection* m_hotspotPropertySection = nullptr; QToolButton* m_btnCreateEntity = nullptr; ToolOptionPopup* m_createEntityPopup = nullptr; QToolButton* m_btnToggleDepthOverlay = nullptr; EditorCanvas* m_editorCanvas = nullptr; QTreeWidget* m_projectTree = nullptr; QStackedWidget* m_rightTopDockStack = nullptr; QListWidget* m_hotspotAnimationList = nullptr; QDockWidget* m_dockProjectTree = nullptr; QDockWidget* m_dockProperties = nullptr; QDockWidget* m_dockTimeline = nullptr; QDockWidget* m_dockResourceLibrary = nullptr; QTreeWidgetItem* m_itemBackground = nullptr; QAction* m_actionUndo = nullptr; QAction* m_actionRedo = nullptr; QAction* m_actionCopy = nullptr; QAction* m_actionPaste = nullptr; QAction* m_actionToggleProjectTree = nullptr; QAction* m_actionToggleProperties = nullptr; QAction* m_actionToggleTimeline = nullptr; QAction* m_actionToggleResourceLibrary = nullptr; QAction* m_actionEnterPreview = nullptr; QAction* m_actionBackToEditor = nullptr; QAction* m_actionAnimationSettings = nullptr; QAction* m_actionCanvasWorldAxes = nullptr; QAction* m_actionCanvasAxisValues = nullptr; QAction* m_actionCanvasGrid = nullptr; QAction* m_actionCanvasCheckerboard = nullptr; QAction* m_actionCanvasDepthOverlay = nullptr; QAction* m_actionCanvasGizmoLabels = nullptr; core::ProjectWorkspace m_workspace; RecentProjectHistory m_recentHistory; bool m_previewRequested = false; /// 因右侧栏过窄自动收起;用户通过视图菜单再次打开时清除 bool m_rightDocksNarrowHidden = false; QPointF m_lastWorldPos; int m_lastWorldZ = -1; bool m_hasSelectedEntity = false; bool m_hasSelectedTool = false; bool m_hasSelectedCamera = false; bool m_syncingTreeSelection = false; int m_selectedEntityDepth = 0; QPointF m_selectedEntityOrigin; QString m_selectedEntityId; QString m_selectedToolId; QString m_selectedCameraId; QString m_selectedBlackholeEntityId; QString m_selectedEntityDisplayNameCache; QString m_bgAbsCache; QString m_bgSizeTextCache; // 项目树“眼睛”:仅用于画布临时隐藏(不持久化、不进时间轴) QSet m_tempHiddenEntityIds; QSet m_tempHiddenToolIds; QSet m_tempHiddenCameraIds; void updateStatusBarText(); void syncCreateEntityToolButtonTooltip(); void refreshPropertyPanel(); void refreshEntityPropertyPanelFast(); void syncProjectTreeFromCanvasSelection(); /// 右侧项目树+属性列最小宽度:随当前属性页与树内容 minimumSizeHint 抬高,窄于内容时不再缩小。 void updateRightDockColumnMinimumWidthFromContent(); bool m_timelineScrubbing = false; /// 限制 scrub 时全量回灌频率,避免每像素一次 evaluate + 磁盘读图导致时间轴极卡 QElapsedTimer m_timelineEditorApplyThrottle; bool m_entityDragging = false; QTimer* m_propertySyncTimer = nullptr; int m_currentFrame = 0; bool m_playing = false; QTimer* m_playTimer = nullptr; QString m_previewAnimationId; bool m_animationRequested = false; bool m_hotspotRequested = false; QString m_selectedHotspotId; QToolButton* m_railBtnMove = nullptr; QToolButton* m_railBtnZoom = nullptr; QToolButton* m_railBtnFit = nullptr; QToolButton* m_btnRailAddHotspot = nullptr; QToolButton* m_btnRailMoveHotspot = nullptr; QToolButton* m_btnHotspotEnterPreview = nullptr; int m_previewFrame = 0; bool m_previewPlaying = false; QTimer* m_previewPlayTimer = nullptr; TimelineWidget* m_timeline = nullptr; TimelineEditorModel* m_timelineModel = nullptr; QToolButton* m_btnPlay = nullptr; QComboBox* m_schemeSelector = nullptr; // 时间轴区间选择(用于逐帧贴图动画) int m_timelineRangeStart = -1; int m_timelineRangeEnd = -1; QCheckBox* m_chkAutoKeyframe = nullptr; // 旧版 DopeSheet 已移除,这里保留占位便于后续扩展区间 UI(如自定义小部件) QTreeWidget* m_dopeTree = nullptr; QPushButton* m_btnDopeDeleteKey = nullptr; gui::EntityIntroPopup* m_entityIntroPopup = nullptr; QToolButton* m_previewBackToStaticBtn = nullptr; gui::ResourceLibraryDock* m_resourceLibraryDockWidget = nullptr; core::library::ResourceLibraryProvider* m_resourceLibraryProvider = nullptr; };