initial commit
This commit is contained in:
2097
client/gui/main_window/MainWindow.cpp
Normal file
2097
client/gui/main_window/MainWindow.cpp
Normal file
File diff suppressed because it is too large
Load Diff
177
client/gui/main_window/MainWindow.h
Normal file
177
client/gui/main_window/MainWindow.h
Normal file
@@ -0,0 +1,177 @@
|
||||
#pragma once
|
||||
|
||||
#include "core/workspace/ProjectWorkspace.h"
|
||||
#include "main_window/RecentProjectHistory.h"
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QPointF>
|
||||
#include <QFrame>
|
||||
#include <QIcon>
|
||||
#include <QTimer>
|
||||
|
||||
class QAction;
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
class QDockWidget;
|
||||
class QFormLayout;
|
||||
class QLabel;
|
||||
class QMenu;
|
||||
class QFrame;
|
||||
class QIcon;
|
||||
class QPushButton;
|
||||
class QSlider;
|
||||
class QStackedWidget;
|
||||
class QToolButton;
|
||||
class QTreeWidget;
|
||||
class QTreeWidgetItem;
|
||||
class QWidget;
|
||||
class EditorCanvas;
|
||||
class TimelineWidget;
|
||||
namespace gui {
|
||||
class BackgroundPropertySection;
|
||||
class EntityPropertySection;
|
||||
}
|
||||
|
||||
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 一律隐藏,视图开关禁用。
|
||||
// - Editor:已打开项目。显示编辑页,按默认规则显示 dock,同时允许用户通过“视图”菜单控制。
|
||||
// - Preview:预览展示。用于全流程完成后的展示(要求:项目已打开且背景不为空)。
|
||||
enum class UiMode { Welcome, Editor, Preview };
|
||||
|
||||
void createMenus(); // 菜单和工具栏
|
||||
void createFileMenu(); // 文件菜单
|
||||
void createEditMenu(); // 编辑菜单
|
||||
void createHelpMenu(); // 帮助菜单
|
||||
void createViewMenu(); // 视图菜单
|
||||
void createProjectTreeDock();
|
||||
void createTimelineDock();
|
||||
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 rebuildCentralPages();
|
||||
void showWelcomePage();
|
||||
void showEditorPage();
|
||||
void showPreviewPage();
|
||||
void refreshWelcomeRecentList();
|
||||
void openProjectFromPath(const QString& dir);
|
||||
void refreshPreviewPage();
|
||||
void refreshEditorPage();
|
||||
void applyTimelineFromProject();
|
||||
void refreshDopeSheet();
|
||||
void setPreviewRequested(bool preview);
|
||||
|
||||
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::EntityPropertySection* m_entityPropertySection = nullptr;
|
||||
QToolButton* m_btnCreateEntity = nullptr;
|
||||
QToolButton* m_btnToggleDepthOverlay = nullptr;
|
||||
|
||||
EditorCanvas* m_editorCanvas = nullptr;
|
||||
|
||||
QTreeWidget* m_projectTree = nullptr;
|
||||
QDockWidget* m_dockProjectTree = nullptr;
|
||||
QDockWidget* m_dockProperties = nullptr;
|
||||
QDockWidget* m_dockTimeline = 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_actionEnterPreview = nullptr;
|
||||
QAction* m_actionBackToEditor = 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_syncingTreeSelection = false;
|
||||
int m_selectedEntityDepth = 0;
|
||||
QPointF m_selectedEntityOrigin;
|
||||
QString m_selectedEntityId;
|
||||
QString m_selectedEntityDisplayNameCache;
|
||||
QString m_bgAbsCache;
|
||||
QString m_bgSizeTextCache;
|
||||
void updateStatusBarText();
|
||||
void refreshPropertyPanel();
|
||||
void refreshEntityPropertyPanelFast();
|
||||
void syncProjectTreeFromCanvasSelection();
|
||||
|
||||
bool m_timelineScrubbing = false;
|
||||
bool m_entityDragging = false;
|
||||
QTimer* m_propertySyncTimer = nullptr;
|
||||
|
||||
int m_currentFrame = 0;
|
||||
bool m_playing = false;
|
||||
QTimer* m_playTimer = nullptr;
|
||||
TimelineWidget* m_timeline = nullptr;
|
||||
QToolButton* m_btnPlay = nullptr;
|
||||
QLabel* m_frameLabel = nullptr;
|
||||
// 时间轴区间选择(用于逐帧贴图动画)
|
||||
int m_timelineRangeStart = -1;
|
||||
int m_timelineRangeEnd = -1;
|
||||
QCheckBox* m_chkAutoKeyframe = nullptr;
|
||||
// 旧版 DopeSheet 已移除,这里保留占位便于后续扩展区间 UI(如自定义小部件)
|
||||
QTreeWidget* m_dopeTree = nullptr;
|
||||
QPushButton* m_btnDopeDeleteKey = nullptr;
|
||||
};
|
||||
100
client/gui/main_window/RecentProjectHistory.cpp
Normal file
100
client/gui/main_window/RecentProjectHistory.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
#include "main_window/RecentProjectHistory.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonValue>
|
||||
#include <QDebug>
|
||||
#include <QStandardPaths>
|
||||
|
||||
QString RecentProjectHistory::cacheFilePath() {
|
||||
const QString base = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation);
|
||||
return QDir(base).filePath(QStringLiteral("landscape_tool/recent_projects.cache"));
|
||||
}
|
||||
|
||||
QString RecentProjectHistory::normalizePath(const QString& path) {
|
||||
if (path.isEmpty()) {
|
||||
return QString();
|
||||
}
|
||||
const QFileInfo fi(path);
|
||||
const QString c = fi.canonicalFilePath();
|
||||
return c.isEmpty() ? QDir::cleanPath(fi.absoluteFilePath()) : c;
|
||||
}
|
||||
|
||||
QStringList RecentProjectHistory::dedupeNewestFirst(const QStringList& paths) {
|
||||
QStringList out;
|
||||
out.reserve(paths.size());
|
||||
for (const QString& p : paths) {
|
||||
const QString n = normalizePath(p);
|
||||
if (n.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (out.contains(n)) {
|
||||
continue;
|
||||
}
|
||||
out.append(n);
|
||||
if (out.size() >= kMaxEntries) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
QStringList RecentProjectHistory::load() const {
|
||||
const QString filePath = cacheFilePath();
|
||||
QFile f(filePath);
|
||||
if (!f.open(QIODevice::ReadOnly)) {
|
||||
return {};
|
||||
}
|
||||
const QJsonDocument doc = QJsonDocument::fromJson(f.readAll());
|
||||
if (!doc.isArray()) {
|
||||
return {};
|
||||
}
|
||||
QStringList paths;
|
||||
for (const QJsonValue& v : doc.array()) {
|
||||
if (v.isString()) {
|
||||
paths.append(v.toString());
|
||||
}
|
||||
}
|
||||
return dedupeNewestFirst(paths);
|
||||
}
|
||||
|
||||
bool RecentProjectHistory::save(const QStringList& paths) const {
|
||||
const QString filePath = cacheFilePath();
|
||||
const QFileInfo fi(filePath);
|
||||
QDir().mkpath(fi.absolutePath());
|
||||
|
||||
QJsonArray arr;
|
||||
for (const QString& p : dedupeNewestFirst(paths)) {
|
||||
arr.append(p);
|
||||
}
|
||||
const QJsonDocument doc(arr);
|
||||
|
||||
QFile f(filePath);
|
||||
if (!f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
||||
qWarning() << "RecentProjectHistory: cannot write" << filePath;
|
||||
return false;
|
||||
}
|
||||
f.write(doc.toJson(QJsonDocument::Compact));
|
||||
return true;
|
||||
}
|
||||
|
||||
void RecentProjectHistory::addAndSave(const QString& projectDir) {
|
||||
const QString n = normalizePath(projectDir);
|
||||
if (n.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
QStringList paths = load();
|
||||
paths.removeAll(n);
|
||||
paths.prepend(n);
|
||||
save(paths);
|
||||
}
|
||||
|
||||
void RecentProjectHistory::removeAndSave(const QString& projectDir) {
|
||||
const QString n = normalizePath(projectDir);
|
||||
QStringList paths = load();
|
||||
paths.removeAll(n);
|
||||
save(paths);
|
||||
}
|
||||
21
client/gui/main_window/RecentProjectHistory.h
Normal file
21
client/gui/main_window/RecentProjectHistory.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
class RecentProjectHistory {
|
||||
public:
|
||||
static constexpr int kMaxEntries = 15;
|
||||
|
||||
static QString cacheFilePath();
|
||||
|
||||
QStringList load() const;
|
||||
bool save(const QStringList& paths) const;
|
||||
void addAndSave(const QString& projectDir);
|
||||
void removeAndSave(const QString& projectDir);
|
||||
|
||||
static QString normalizePath(const QString& path);
|
||||
|
||||
private:
|
||||
static QStringList dedupeNewestFirst(const QStringList& paths);
|
||||
};
|
||||
Reference in New Issue
Block a user