* refactor(engine): 重构2D渲染管线坐标系统 * feat(engine): 完善2D渲染管线和编辑器视口功能 * feat(editor): 实现Viewport变换工具系统 * feat(editor): 优化Inspector渲染性能并修复Gizmo变换工具显示 * feat(editor): 实现Run on Device移动预览功能 * feat(editor): 添加组件属性控制和依赖关系系统 * feat(editor): 实现动画预览功能和优化SpriteAnimator编辑器 * feat(editor): 修复SpriteAnimator动画预览功能并迁移CI到pnpm * feat(editor): 修复SpriteAnimator动画预览并迁移到pnpm * feat(editor): 修复SpriteAnimator动画预览并迁移到pnpm * feat(editor): 修复SpriteAnimator动画预览并迁移到pnpm * feat(editor): 修复SpriteAnimator动画预览并迁移到pnpm * feat(ci): 迁移项目到pnpm并修复CI构建问题 * chore: 迁移CI工作流到pnpm并添加WASM构建支持 * chore: 迁移CI工作流到pnpm并添加WASM构建支持 * chore: 迁移CI工作流到pnpm并添加WASM构建支持 * chore: 迁移CI工作流到pnpm并添加WASM构建支持 * chore: 迁移CI工作流到pnpm并添加WASM构建支持 * chore: 迁移CI工作流到pnpm并添加WASM构建支持 * chore: 移除 network 相关包 * chore: 移除 network 相关包
304 lines
10 KiB
TypeScript
304 lines
10 KiB
TypeScript
import React from 'react';
|
|
import { Play, Pause, Square, SkipForward, RotateCcw, Trash2, Undo, Redo, Box } from 'lucide-react';
|
|
|
|
type ExecutionMode = 'idle' | 'running' | 'paused' | 'step';
|
|
|
|
interface EditorToolbarProps {
|
|
executionMode: ExecutionMode;
|
|
canUndo: boolean;
|
|
canRedo: boolean;
|
|
showGizmos: boolean;
|
|
onPlay: () => void;
|
|
onPause: () => void;
|
|
onStop: () => void;
|
|
onStep: () => void;
|
|
onReset: () => void;
|
|
onUndo: () => void;
|
|
onRedo: () => void;
|
|
onResetView: () => void;
|
|
onClearCanvas: () => void;
|
|
onToggleGizmos: () => void;
|
|
}
|
|
|
|
export const EditorToolbar: React.FC<EditorToolbarProps> = ({
|
|
executionMode,
|
|
canUndo,
|
|
canRedo,
|
|
showGizmos,
|
|
onPlay,
|
|
onPause,
|
|
onStop,
|
|
onStep,
|
|
onReset,
|
|
onUndo,
|
|
onRedo,
|
|
onResetView,
|
|
onClearCanvas,
|
|
onToggleGizmos
|
|
}) => {
|
|
return (
|
|
<div style={{
|
|
position: 'absolute',
|
|
top: '10px',
|
|
left: '50%',
|
|
transform: 'translateX(-50%)',
|
|
display: 'flex',
|
|
gap: '8px',
|
|
backgroundColor: 'rgba(45, 45, 45, 0.95)',
|
|
padding: '8px',
|
|
borderRadius: '6px',
|
|
boxShadow: '0 2px 8px rgba(0,0,0,0.3)',
|
|
zIndex: 100
|
|
}}>
|
|
{/* 播放按钮 */}
|
|
<button
|
|
onClick={onPlay}
|
|
disabled={executionMode === 'running'}
|
|
style={{
|
|
padding: '8px',
|
|
backgroundColor: executionMode === 'running' ? '#2d2d2d' : '#4caf50',
|
|
border: 'none',
|
|
borderRadius: '4px',
|
|
color: executionMode === 'running' ? '#666' : '#fff',
|
|
cursor: executionMode === 'running' ? 'not-allowed' : 'pointer',
|
|
fontSize: '14px',
|
|
fontWeight: 'bold',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center'
|
|
}}
|
|
title="运行 (Play)"
|
|
>
|
|
<Play size={16} />
|
|
</button>
|
|
|
|
{/* 暂停按钮 */}
|
|
<button
|
|
onClick={onPause}
|
|
disabled={executionMode === 'idle'}
|
|
style={{
|
|
padding: '8px',
|
|
backgroundColor: executionMode === 'idle' ? '#2d2d2d' : '#ff9800',
|
|
border: 'none',
|
|
borderRadius: '4px',
|
|
color: executionMode === 'idle' ? '#666' : '#fff',
|
|
cursor: executionMode === 'idle' ? 'not-allowed' : 'pointer',
|
|
fontSize: '14px',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center'
|
|
}}
|
|
title={executionMode === 'paused' ? '继续' : '暂停'}
|
|
>
|
|
{executionMode === 'paused' ? <Play size={16} /> : <Pause size={16} />}
|
|
</button>
|
|
|
|
{/* 停止按钮 */}
|
|
<button
|
|
onClick={onStop}
|
|
disabled={executionMode === 'idle'}
|
|
style={{
|
|
padding: '8px',
|
|
backgroundColor: executionMode === 'idle' ? '#2d2d2d' : '#f44336',
|
|
border: 'none',
|
|
borderRadius: '4px',
|
|
color: executionMode === 'idle' ? '#666' : '#fff',
|
|
cursor: executionMode === 'idle' ? 'not-allowed' : 'pointer',
|
|
fontSize: '14px',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center'
|
|
}}
|
|
title="停止"
|
|
>
|
|
<Square size={16} />
|
|
</button>
|
|
|
|
{/* 单步执行按钮 */}
|
|
<button
|
|
onClick={onStep}
|
|
disabled={executionMode !== 'idle' && executionMode !== 'paused'}
|
|
style={{
|
|
padding: '8px',
|
|
backgroundColor: (executionMode !== 'idle' && executionMode !== 'paused') ? '#2d2d2d' : '#2196f3',
|
|
border: 'none',
|
|
borderRadius: '4px',
|
|
color: (executionMode !== 'idle' && executionMode !== 'paused') ? '#666' : '#fff',
|
|
cursor: (executionMode !== 'idle' && executionMode !== 'paused') ? 'not-allowed' : 'pointer',
|
|
fontSize: '14px',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center'
|
|
}}
|
|
title="单步执行"
|
|
>
|
|
<SkipForward size={16} />
|
|
</button>
|
|
|
|
{/* 重置按钮 */}
|
|
<button
|
|
onClick={onReset}
|
|
style={{
|
|
padding: '8px',
|
|
backgroundColor: '#9e9e9e',
|
|
border: 'none',
|
|
borderRadius: '4px',
|
|
color: '#fff',
|
|
cursor: 'pointer',
|
|
fontSize: '14px',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center'
|
|
}}
|
|
title="重置"
|
|
>
|
|
<RotateCcw size={16} />
|
|
</button>
|
|
|
|
{/* 分隔符 */}
|
|
<div style={{
|
|
width: '1px',
|
|
backgroundColor: '#666',
|
|
margin: '4px 0'
|
|
}} />
|
|
|
|
{/* 重置视图按钮 */}
|
|
<button
|
|
onClick={onResetView}
|
|
style={{
|
|
padding: '8px 12px',
|
|
backgroundColor: '#3c3c3c',
|
|
border: 'none',
|
|
borderRadius: '4px',
|
|
color: '#cccccc',
|
|
cursor: 'pointer',
|
|
fontSize: '12px',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
gap: '4px'
|
|
}}
|
|
title="重置视图 (滚轮缩放, Alt+拖动平移)"
|
|
>
|
|
<RotateCcw size={14} />
|
|
View
|
|
</button>
|
|
|
|
{/* 清空画布按钮 */}
|
|
<button
|
|
style={{
|
|
padding: '8px 12px',
|
|
backgroundColor: '#3c3c3c',
|
|
border: 'none',
|
|
borderRadius: '4px',
|
|
color: '#cccccc',
|
|
cursor: 'pointer',
|
|
fontSize: '12px',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
gap: '4px'
|
|
}}
|
|
title="清空画布"
|
|
onClick={onClearCanvas}
|
|
>
|
|
<Trash2 size={14} />
|
|
清空
|
|
</button>
|
|
|
|
{/* Gizmo 开关按钮 */}
|
|
<button
|
|
onClick={onToggleGizmos}
|
|
style={{
|
|
padding: '8px 12px',
|
|
backgroundColor: showGizmos ? '#4a9eff' : '#3c3c3c',
|
|
border: 'none',
|
|
borderRadius: '4px',
|
|
color: showGizmos ? '#fff' : '#cccccc',
|
|
cursor: 'pointer',
|
|
fontSize: '12px',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
gap: '4px'
|
|
}}
|
|
title="显示/隐藏选择边框 (Gizmos)"
|
|
>
|
|
<Box size={14} />
|
|
Gizmos
|
|
</button>
|
|
|
|
{/* 分隔符 */}
|
|
<div style={{
|
|
width: '1px',
|
|
height: '24px',
|
|
backgroundColor: '#555',
|
|
margin: '0 4px'
|
|
}} />
|
|
|
|
{/* 撤销按钮 */}
|
|
<button
|
|
onClick={onUndo}
|
|
disabled={!canUndo}
|
|
style={{
|
|
padding: '8px',
|
|
backgroundColor: canUndo ? '#3c3c3c' : '#2d2d2d',
|
|
border: 'none',
|
|
borderRadius: '4px',
|
|
color: canUndo ? '#cccccc' : '#666',
|
|
cursor: canUndo ? 'pointer' : 'not-allowed',
|
|
fontSize: '14px',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center'
|
|
}}
|
|
title="撤销 (Ctrl+Z)"
|
|
>
|
|
<Undo size={16} />
|
|
</button>
|
|
|
|
{/* 重做按钮 */}
|
|
<button
|
|
onClick={onRedo}
|
|
disabled={!canRedo}
|
|
style={{
|
|
padding: '8px',
|
|
backgroundColor: canRedo ? '#3c3c3c' : '#2d2d2d',
|
|
border: 'none',
|
|
borderRadius: '4px',
|
|
color: canRedo ? '#cccccc' : '#666',
|
|
cursor: canRedo ? 'pointer' : 'not-allowed',
|
|
fontSize: '14px',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center'
|
|
}}
|
|
title="重做 (Ctrl+Shift+Z / Ctrl+Y)"
|
|
>
|
|
<Redo size={16} />
|
|
</button>
|
|
|
|
{/* 状态指示器 */}
|
|
<div style={{
|
|
padding: '8px 12px',
|
|
backgroundColor: '#1e1e1e',
|
|
borderRadius: '4px',
|
|
fontSize: '12px',
|
|
color: '#ccc',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
gap: '6px'
|
|
}}>
|
|
<span style={{
|
|
width: '8px',
|
|
height: '8px',
|
|
borderRadius: '50%',
|
|
backgroundColor:
|
|
executionMode === 'running' ? '#4caf50' :
|
|
executionMode === 'paused' ? '#ff9800' : '#666'
|
|
}} />
|
|
{executionMode === 'idle' ? 'Idle' :
|
|
executionMode === 'running' ? 'Running' :
|
|
executionMode === 'paused' ? 'Paused' : 'Step'}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|