feat(vivid2D): 实现多选图层与文件拖放功能
- 添加 JnaFileChooser 库支持,替换原有 JFileChooser - 实现图层面板的多选功能与批量操作 - 支持通过拖放方式导入 PSD 和图片文件 - 新增新建模型功能,完善文件菜单选项 -优化模型加载逻辑,支持直接加载 Model2D 对象 - 重构图层重排序逻辑,支持多图层块移动- 改进鼠标点击与悬停事件处理机制 - 修复图层操作后选中状态与缩略图刷新问题 - 添加命令行启动任务 runBoxClient与 runVivid2DClient - 升级主窗口初始化流程与界面组件配置
This commit is contained in:
@@ -1,17 +1,15 @@
|
||||
package com.chuangzhou.vivid2D.window;
|
||||
|
||||
import com.chuangzhou.vivid2D.render.awt.ModelLayerPanel;
|
||||
import com.chuangzhou.vivid2D.render.awt.ModelRenderPanel;
|
||||
import com.chuangzhou.vivid2D.render.awt.ParametersPanel;
|
||||
import com.chuangzhou.vivid2D.render.awt.TransformPanel;
|
||||
import com.chuangzhou.vivid2D.render.awt.*;
|
||||
import com.chuangzhou.vivid2D.render.awt.manager.LayerOperationManager;
|
||||
import com.chuangzhou.vivid2D.render.awt.manager.ParametersManagement;
|
||||
import com.chuangzhou.vivid2D.render.awt.manager.data.LayerOperationManagerData;
|
||||
import com.chuangzhou.vivid2D.render.awt.manager.data.ParametersManagementData;
|
||||
import com.chuangzhou.vivid2D.render.awt.ModelPartInfoPanel;
|
||||
import com.chuangzhou.vivid2D.render.model.Model2D;
|
||||
import com.chuangzhou.vivid2D.render.model.ModelPart;
|
||||
import com.chuangzhou.vivid2D.render.model.util.Mesh2D;
|
||||
import com.formdev.flatlaf.themes.FlatMacDarkLaf;
|
||||
import jnafilechooser.api.JnaFileChooser;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
@@ -43,28 +41,6 @@ public class MainWindow extends JFrame {
|
||||
private JLabel statusBarLabel;
|
||||
private JMenuBar menuBar;
|
||||
|
||||
/**
|
||||
* 启动器。
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
// 设置 Look and Feel
|
||||
try {
|
||||
UIManager.setLookAndFeel(new FlatMacDarkLaf());
|
||||
} catch (UnsupportedLookAndFeelException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
// 确保控制台输出使用 UTF-8
|
||||
System.setOut(new PrintStream(System.out, true, StandardCharsets.UTF_8));
|
||||
System.setErr(new PrintStream(System.err, true, StandardCharsets.UTF_8));
|
||||
|
||||
// 在 EDT (Event Dispatch Thread) 上创建和显示 GUI
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
MainWindow mainWin = new MainWindow();
|
||||
mainWin.setVisible(true);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造主窗口。
|
||||
*/
|
||||
@@ -72,30 +48,17 @@ public class MainWindow extends JFrame {
|
||||
setTitle("Vivid2D Editor - [未加载文件]");
|
||||
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
|
||||
setLayout(new BorderLayout());
|
||||
|
||||
// 1. 初始化核心渲染器和面板
|
||||
// ModelRenderPanel 传入空路径 ""
|
||||
this.renderPanel = new ModelRenderPanel("", 1024, 768);
|
||||
this.layerPanel = new ModelLayerPanel(renderPanel);
|
||||
this.transformPanel = new TransformPanel(renderPanel);
|
||||
this.parametersPanel = new ParametersPanel(renderPanel);
|
||||
// 【重要】使用我们新实现的 ModelPartInfoPanel
|
||||
this.partInfoPanel = new ModelPartInfoPanel(renderPanel);
|
||||
|
||||
// 关联参数管理器
|
||||
//renderPanel.setParametersManagement(ParametersManagement.getInstance(parametersPanel));
|
||||
|
||||
// 2. 构建模块化的 UI
|
||||
createMenuBar();
|
||||
createToolBar();
|
||||
createMainLayout();
|
||||
createStatusBar();
|
||||
|
||||
// 3. 设置初始状态:所有编辑功能禁用
|
||||
setEditComponentsEnabled(false);
|
||||
setupInitialListeners();
|
||||
|
||||
// 4. 设置窗口
|
||||
setSize(1600, 900);
|
||||
setLocationRelativeTo(null);
|
||||
}
|
||||
@@ -106,17 +69,26 @@ public class MainWindow extends JFrame {
|
||||
private void createMenuBar() {
|
||||
menuBar = new JMenuBar();
|
||||
JMenu fileMenu = new JMenu("文件");
|
||||
|
||||
// 新增:新建模型菜单项
|
||||
JMenuItem newItem = new JMenuItem("新建模型...");
|
||||
newItem.addActionListener(e -> createNewModel());
|
||||
fileMenu.add(newItem);
|
||||
|
||||
JMenuItem openItem = new JMenuItem("打开模型...");
|
||||
openItem.addActionListener(e -> openModelFile());
|
||||
fileMenu.add(openItem);
|
||||
fileMenu.addSeparator();
|
||||
|
||||
JMenuItem saveItem = new JMenuItem("保存");
|
||||
saveItem.setName("saveItem");
|
||||
saveItem.addActionListener(e -> saveData(false));
|
||||
fileMenu.add(saveItem);
|
||||
|
||||
JMenuItem exitItem = new JMenuItem("退出");
|
||||
exitItem.addActionListener(e -> dispatchEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING)));
|
||||
fileMenu.add(exitItem);
|
||||
|
||||
menuBar.add(fileMenu);
|
||||
JMenu editMenu = new JMenu("编辑");
|
||||
editMenu.setName("editMenu");
|
||||
@@ -125,6 +97,45 @@ public class MainWindow extends JFrame {
|
||||
setJMenuBar(menuBar);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理新建模型的操作。
|
||||
*/
|
||||
private void createNewModel() {
|
||||
String modelName = JOptionPane.showInputDialog(this, "请输入新模型的名称:", "新建模型", JOptionPane.PLAIN_MESSAGE);
|
||||
if (modelName != null && !modelName.trim().isEmpty()) {
|
||||
modelName = modelName.trim();
|
||||
String finalModelName = modelName;
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
Model2D newModel = new Model2D(finalModelName);
|
||||
setEditComponentsEnabled(false);
|
||||
statusBarLabel.setText("正在创建并加载新模型: " + finalModelName);
|
||||
try {
|
||||
renderPanel.loadModel(newModel);
|
||||
renderPanel.setParametersManagement(ParametersManagement.getInstance(parametersPanel));
|
||||
layerPanel.loadMetadata();
|
||||
currentModelPath = null;
|
||||
setTitle("Vivid2D Editor - " + finalModelName + " [新建]");
|
||||
statusBarLabel.setText("新模型 " + finalModelName + " 创建并加载完毕。");
|
||||
setEditComponentsEnabled(true);
|
||||
layerPanel.setModel(newModel);
|
||||
} catch (Exception e) {
|
||||
System.err.println("新建模型加载失败: " + e.getMessage());
|
||||
currentModelPath = null;
|
||||
setTitle("Vivid2D Editor - [加载失败]");
|
||||
statusBarLabel.setText("新模型加载失败!无法加载: " + finalModelName);
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"无法加载新模型: " + finalModelName + "\n错误: " + e.getMessage(),
|
||||
"加载错误",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
setEditComponentsEnabled(false);
|
||||
}
|
||||
});
|
||||
} else if (modelName != null) {
|
||||
JOptionPane.showMessageDialog(this, "模型名称不能为空。", "输入错误", JOptionPane.WARNING_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 创建顶部工具栏。
|
||||
*/
|
||||
@@ -225,16 +236,25 @@ public class MainWindow extends JFrame {
|
||||
}
|
||||
});
|
||||
|
||||
renderPanel.addModelClickListener((mesh, modelX, modelY, screenX, screenY) -> {
|
||||
List<ModelPart> selectedPart = renderPanel.getSelectedParts();
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
transformPanel.setSelectedParts(selectedPart);
|
||||
if (!selectedPart.isEmpty()) {
|
||||
partInfoPanel.updatePanel(selectedPart.get(0));
|
||||
} else {
|
||||
partInfoPanel.updatePanel(null);
|
||||
}
|
||||
});
|
||||
renderPanel.addModelClickListener(new ModelClickListener() {
|
||||
@Override
|
||||
public void onModelClicked(Mesh2D mesh, float modelX, float modelY, int screenX, int screenY) {
|
||||
List<ModelPart> selectedPart = renderPanel.getSelectedParts();
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
layerPanel.setSelectedLayers(selectedPart);
|
||||
transformPanel.setSelectedParts(selectedPart);
|
||||
if (!selectedPart.isEmpty()) {
|
||||
partInfoPanel.updatePanel(selectedPart.get(0));
|
||||
} else {
|
||||
partInfoPanel.updatePanel(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onModelHover(Mesh2D mesh, float modelX, float modelY, int screenX, int screenY) {
|
||||
onModelClicked(mesh, modelX, modelY, screenX, screenY);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -274,13 +294,13 @@ public class MainWindow extends JFrame {
|
||||
* 打开文件对话框并加载模型。
|
||||
*/
|
||||
private void openModelFile() {
|
||||
JFileChooser fileChooser = new JFileChooser();
|
||||
fileChooser.setDialogTitle("选择 Vivid2D 模型文件 (*.model)");
|
||||
FileNameExtensionFilter filter = new FileNameExtensionFilter("Vivid2D 模型文件 (*.model)", "model");
|
||||
fileChooser.setFileFilter(filter);
|
||||
fileChooser.setAcceptAllFileFilterUsed(false); // 这一行可选,用于禁用 "All Files" 选项
|
||||
if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
|
||||
File file = fileChooser.getSelectedFile();
|
||||
JnaFileChooser jnaFileChooser = new JnaFileChooser();
|
||||
jnaFileChooser.setTitle("选择 Vivid2D 模型文件 (*.model)");
|
||||
jnaFileChooser.addFilter("Vivid2D 模型文件 (*.model)", "model");
|
||||
jnaFileChooser.setMultiSelectionEnabled(false);
|
||||
jnaFileChooser.setMode(JnaFileChooser.Mode.Files);
|
||||
if (jnaFileChooser.showOpenDialog(this)) {
|
||||
File file = jnaFileChooser.getSelectedFile();
|
||||
loadModel(file.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
@@ -294,6 +314,7 @@ public class MainWindow extends JFrame {
|
||||
CompletableFuture.runAsync(() -> {
|
||||
Model2D model = null;
|
||||
try {
|
||||
// 假设 renderPanel.loadModel(String modelPath) 返回一个 CompletableFuture<Model2D>
|
||||
model = renderPanel.loadModel(modelPath).get();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
System.err.println("模型异步加载失败: " + e.getMessage());
|
||||
@@ -327,8 +348,32 @@ public class MainWindow extends JFrame {
|
||||
*/
|
||||
private void saveData(boolean exitOnComplete) {
|
||||
if (currentModelPath == null) {
|
||||
statusBarLabel.setText("没有加载模型,无法保存。");
|
||||
return;
|
||||
JnaFileChooser jnaFileChooser = new JnaFileChooser();
|
||||
jnaFileChooser.setTitle("另存为 Vivid2D 模型文件 (*.model)");
|
||||
|
||||
// JnaFileChooser 使用 addFilter() 来添加过滤器
|
||||
jnaFileChooser.addFilter("Vivid2D 模型文件 (*.model)", "model");
|
||||
jnaFileChooser.setMultiSelectionEnabled(false);
|
||||
jnaFileChooser.setOpenButtonText("保存");
|
||||
jnaFileChooser.setMode(JnaFileChooser.Mode.Files);
|
||||
|
||||
// 弹出保存对话框
|
||||
if (jnaFileChooser.showSaveDialog(this)) {
|
||||
File fileToSave = jnaFileChooser.getSelectedFile();
|
||||
String path = fileToSave.getAbsolutePath();
|
||||
|
||||
// 确保文件以 .model 结尾 (原生对话框可能已经处理,但 Swing 风格代码保留以防万一)
|
||||
if (!path.toLowerCase().endsWith(".model")) {
|
||||
path += ".model";
|
||||
fileToSave = new File(path);
|
||||
}
|
||||
|
||||
this.currentModelPath = path;
|
||||
setTitle("Vivid2D Editor - " + fileToSave.getName());
|
||||
} else {
|
||||
statusBarLabel.setText("保存操作已取消。");
|
||||
return;
|
||||
}
|
||||
}
|
||||
statusBarLabel.setText("正在保存...");
|
||||
if (renderPanel.getModel() != null) {
|
||||
|
||||
Reference in New Issue
Block a user