94 lines
118 KiB
XML
94 lines
118 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<project version="4">
|
|
<component name="CopilotDiffPersistence">
|
|
<option name="pendingDiffs">
|
|
<map>
|
|
<entry key="$PROJECT_DIR$/pom.xml">
|
|
<value>
|
|
<PendingDiffInfo>
|
|
<option name="filePath" value="$PROJECT_DIR$/pom.xml" />
|
|
<option name="originalContent" value="<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>dev.bytevibe</groupId> <artifactId>hyperpoint</artifactId> <version>1.0-SNAPSHOT</version> <name>hyperpoint</name> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <junit.version>5.12.1</junit.version> <javafx.version>25.0.1</javafx.version> </properties> <dependencies> <dependency> <groupId>org.openjfx</groupId> <artifactId>javafx-controls</artifactId> <version>${javafx.version}</version> </dependency> <dependency> <groupId>org.openjfx</groupId> <artifactId>javafx-fxml</artifactId> <version>${javafx.version}</version> </dependency> <dependency> <groupId>org.openjfx</groupId> <artifactId>javafx-swing</artifactId> <version>${javafx.version}</version> </dependency> <!-- Gson库,用于JSON序列化 --> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.10.1</version> </dependency> <!-- iText库,用于PDF生成 --> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itextpdf</artifactId> <version>5.5.13.3</version> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.13.0</version> <configuration> <source>25</source> <target>25</target> </configuration> </plugin> <plugin> <groupId>org.openjfx</groupId> <artifactId>javafx-maven-plugin</artifactId> <version>0.0.8</version> <executions> <execution> <!-- Default configuration for running with: mvn clean javafx:run --> <id>default-cli</id> <configuration> <mainClass>dev.bytevibe.hyperpoint/dev.bytevibe.hyperpoint.Main</mainClass> <launcher>app</launcher> <jlinkZipName>app</jlinkZipName> <jlinkImageName>app</jlinkImageName> <noManPages>true</noManPages> <stripDebug>true</stripDebug> <noHeaderFiles>true</noHeaderFiles> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>" />
|
|
<option name="updatedContent" value="<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>dev.bytevibe</groupId> <artifactId>hyperpoint</artifactId> <version>1.0-SNAPSHOT</version> <name>hyperpoint</name> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <junit.version>5.12.1</junit.version> <javafx.version>25.0.1</javafx.version> </properties> <dependencies> <dependency> <groupId>org.openjfx</groupId> <artifactId>javafx-controls</artifactId> <version>${javafx.version}</version> </dependency> <dependency> <groupId>org.openjfx</groupId> <artifactId>javafx-fxml</artifactId> <version>${javafx.version}</version> </dependency> <dependency> <groupId>org.openjfx</groupId> <artifactId>javafx-graphics</artifactId> <version>${javafx.version}</version> </dependency> <dependency> <groupId>org.openjfx</groupId> <artifactId>javafx-swing</artifactId> <version>${javafx.version}</version> </dependency> <!-- Gson库,用于JSON序列化 --> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.10.1</version> </dependency> <!-- iText库,用于PDF生成 --> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itextpdf</artifactId> <version>5.5.13.3</version> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.13.0</version> <configuration> <source>25</source> <target>25</target> </configuration> </plugin> <plugin> <groupId>org.openjfx</groupId> <artifactId>javafx-maven-plugin</artifactId> <version>0.0.8</version> <executions> <execution> <!-- Default configuration for running with: mvn clean javafx:run --> <id>default-cli</id> <configuration> <mainClass>dev.bytevibe.hyperpoint/dev.bytevibe.hyperpoint.Main</mainClass> <launcher>app</launcher> <jlinkZipName>app</jlinkZipName> <jlinkImageName>app</jlinkImageName> <noManPages>true</noManPages> <stripDebug>true</stripDebug> <noHeaderFiles>true</noHeaderFiles> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>" />
|
|
</PendingDiffInfo>
|
|
</value>
|
|
</entry>
|
|
<entry key="$PROJECT_DIR$/src/main/java/dev/bytevibe/hyperpoint/AddObjectCommand.java">
|
|
<value>
|
|
<PendingDiffInfo>
|
|
<option name="filePath" value="$PROJECT_DIR$/src/main/java/dev/bytevibe/hyperpoint/AddObjectCommand.java" />
|
|
<option name="updatedContent" value="package dev.bytevibe.hyperpoint; /** * 添加对象命令 */ public class AddObjectCommand implements Command { private PageContent pageContent; private DrawableObject object; public AddObjectCommand(PageContent pageContent, DrawableObject object) { this.pageContent = pageContent; this.object = object; } @Override public void execute() { pageContent.addObject(object); } @Override public void undo() { pageContent.removeObject(object); } } " />
|
|
</PendingDiffInfo>
|
|
</value>
|
|
</entry>
|
|
<entry key="$PROJECT_DIR$/src/main/java/dev/bytevibe/hyperpoint/Command.java">
|
|
<value>
|
|
<PendingDiffInfo>
|
|
<option name="filePath" value="$PROJECT_DIR$/src/main/java/dev/bytevibe/hyperpoint/Command.java" />
|
|
<option name="updatedContent" value="package dev.bytevibe.hyperpoint; /** * 命令接口,用于撤销和重做功能 */ public interface Command { /** * 执行命令 */ void execute(); /** * 撤销命令 */ void undo(); } " />
|
|
</PendingDiffInfo>
|
|
</value>
|
|
</entry>
|
|
<entry key="$PROJECT_DIR$/src/main/java/dev/bytevibe/hyperpoint/CommandHistory.java">
|
|
<value>
|
|
<PendingDiffInfo>
|
|
<option name="filePath" value="$PROJECT_DIR$/src/main/java/dev/bytevibe/hyperpoint/CommandHistory.java" />
|
|
<option name="updatedContent" value="package dev.bytevibe.hyperpoint; import java.util.Stack; /** * 命令历史管理器,支持撤销和重做 */ public class CommandHistory { private Stack<Command> undoStack; private Stack<Command> redoStack; private Runnable onHistoryChanged; public CommandHistory() { this.undoStack = new Stack<>(); this.redoStack = new Stack<>(); } /** * 执行命令并将其添加到历史记录 */ public void execute(Command command) { command.execute(); undoStack.push(command); redoStack.clear(); // 执行新命令后,清空重做栈 notifyHistoryChanged(); } /** * 撤销上一个命令 */ public void undo() { if (!undoStack.isEmpty()) { Command command = undoStack.pop(); command.undo(); redoStack.push(command); notifyHistoryChanged(); } } /** * 重做上一个被撤销的命令 */ public void redo() { if (!redoStack.isEmpty()) { Command command = redoStack.pop(); command.execute(); undoStack.push(command); notifyHistoryChanged(); } } /** * 检查是否可以撤销 */ public boolean canUndo() { return !undoStack.isEmpty(); } /** * 检查是否可以重做 */ public boolean canRedo() { return !redoStack.isEmpty(); } /** * 清空历史记录 */ public void clear() { undoStack.clear(); redoStack.clear(); notifyHistoryChanged(); } /** * 设置历史记录变化监听器 */ public void setOnHistoryChanged(Runnable callback) { this.onHistoryChanged = callback; } /** * 通知历史记录已改变 */ private void notifyHistoryChanged() { if (onHistoryChanged != null) { onHistoryChanged.run(); } } } " />
|
|
</PendingDiffInfo>
|
|
</value>
|
|
</entry>
|
|
<entry key="$PROJECT_DIR$/src/main/java/dev/bytevibe/hyperpoint/Controller.java">
|
|
<value>
|
|
<PendingDiffInfo>
|
|
<option name="filePath" value="$PROJECT_DIR$/src/main/java/dev/bytevibe/hyperpoint/Controller.java" />
|
|
<option name="originalContent" value="package dev.bytevibe.hyperpoint; import dev.bytevibe.hyperpoint.Utils.MyAlert; import dev.bytevibe.hyperpoint.Utils.MyTextInputDialog; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.*; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.VBox; import javafx.stage.FileChooser; import javafx.stage.Stage; import java.io.File; import java.net.URL; import java.util.ResourceBundle; public class Controller implements Initializable { @FXML private ListView<SlidePage> pageListView; @FXML private Label pageNameLabel; @FXML private AnchorPane drawingCanvasContainer; @FXML private VBox propertyPanelContainer; @FXML private AnchorPane scenePane; private Slide currentSlide; private DrawingCanvas drawingCanvas; private PropertyPanel propertyPanelComponent; Stage stage; @Override public void initialize(URL url, ResourceBundle resourceBundle) { pageListView.setDisable(true); // 添加页面列表选择监听 pageListView.getSelectionModel().selectedItemProperty().addListener((obs, oldVal, newVal) -> { if (newVal != null) { displayPageContent(newVal); } }); } /** * 新建幻灯片按钮的事件处理 */ @FXML public void onNewSlide(ActionEvent actionEvent) { MyTextInputDialog dialog = new MyTextInputDialog("我的幻灯片"); dialog.setTitle("创建幻灯片"); dialog.setHeaderText("请输入幻灯片名称"); dialog.setContentText("幻灯片名称:"); if (dialog.showAndWait().isPresent()) { String slideName = dialog.getResult(); if (!slideName.trim().isEmpty()) { currentSlide = new Slide(slideName); loadSlidePages(); MyAlert alert = new MyAlert(Alert.AlertType.INFORMATION); alert.setTitle("成功"); alert.setHeaderText("幻灯片已创建"); alert.setContentText("幻灯片 \"" + slideName + "\" 已创建。"); stage = (Stage) scenePane.getScene().getWindow(); if (stage != null) { stage.setTitle("Hyperpoint - " + slideName); } alert.showAndWait(); } else { showWarning("无效名称", "幻灯片名称不能为空。"); } } } /** * 加载幻灯片的所有页面到列表中 */ private void loadSlidePages() { if (currentSlide != null) { pageListView.setItems(currentSlide.getPages()); pageListView.setDisable(false); if (!currentSlide.getPages().isEmpty()) { pageListView.getSelectionModel().selectFirst(); } } } /** * 显示页面内容 */ private void displayPageContent(SlidePage page) { if (page != null) { pageNameLabel.setText("页面名称: " + page.getTitle()); // 移除旧的Canvas drawingCanvasContainer.getChildren().clear(); propertyPanelContainer.getChildren().clear(); // 创建新的Canvas PageContent pageContent = page.getPageContent(); drawingCanvas = new DrawingCanvas(pageContent); drawingCanvasContainer.getChildren().add(drawingCanvas); // 使用AnchorPane的约束来填充容器 AnchorPane.setTopAnchor(drawingCanvas, 0.0); AnchorPane.setLeftAnchor(drawingCanvas, 0.0); AnchorPane.setRightAnchor(drawingCanvas, 0.0); AnchorPane.setBottomAnchor(drawingCanvas, 0.0); // 创建属性面板 propertyPanelComponent = new PropertyPanel(drawingCanvas); propertyPanelContainer.getChildren().add(propertyPanelComponent); } } /** * 新建页面按钮的事件处理 */ @FXML public void onNewPage(ActionEvent actionEvent) { if (currentSlide == null) { showWarning("无幻灯片", "请先打开或创建一个幻灯片。"); return; } MyTextInputDialog dialog = new MyTextInputDialog("页面 " + (currentSlide.getPages().size() + 1)); dialog.setTitle("新建页面"); dialog.setHeaderText("请输入页面名称"); dialog.setContentText("页面名称:"); if (dialog.showAndWait().isPresent()) { String pageName = dialog.getResult(); if (!pageName.trim().isEmpty()) { SlidePage newPage = new SlidePage(pageName); currentSlide.addPage(newPage); pageListView.refresh(); pageListView.getSelectionModel().selectLast(); MyAlert alert = new MyAlert(Alert.AlertType.INFORMATION); alert.setTitle("成功"); alert.setHeaderText("页面已创建"); alert.setContentText("新页面 \"" + pageName + "\" 已添加。"); alert.showAndWait(); } } } /** * 删除页面按钮的事件处理 */ @FXML public void onDeletePage(ActionEvent actionEvent) { if (currentSlide == null || currentSlide.getPages().isEmpty()) { showWarning("无法删除", "没有可删除的页面。"); return; } int selectedIndex = pageListView.getSelectionModel().getSelectedIndex(); if (selectedIndex < 0) { showWarning("请选择页面", "请先选择要删除的页面。"); return; } Alert confirmAlert = new Alert(Alert.AlertType.CONFIRMATION); confirmAlert.setTitle("确认删除"); confirmAlert.setHeaderText("确认删除该页面?"); confirmAlert.setContentText("删除后无法恢复,确定继续吗?"); var result = confirmAlert.showAndWait(); if (result.isPresent() && result.get() == ButtonType.OK) { if (currentSlide.removePage(selectedIndex)) { pageListView.refresh(); if (selectedIndex >= currentSlide.getPages().size()) { pageListView.getSelectionModel().selectLast(); } else { pageListView.getSelectionModel().select(selectedIndex); } Alert alert = new Alert(Alert.AlertType.INFORMATION); alert.setTitle("成功"); alert.setHeaderText("页面已删除"); alert.showAndWait(); } else { showWarning("无法删除", "幻灯片至少需要保留一个页面。"); } } } /** * 添加文本按钮的事件处理 */ @FXML public void onAddText(ActionEvent actionEvent) { if (drawingCanvas == null) { showWarning("请先选择页面", "请先选择一个页面。"); return; } TextInputDialog dialog = new TextInputDialog("输入文本"); dialog.setTitle("添加文本"); dialog.setHeaderText("请输入要添加的文本"); dialog.setContentText("文本内容:"); if (dialog.showAndWait().isPresent()) { String text = dialog.getResult(); if (!text.trim().isEmpty()) { TextObject textObj = new TextObject(50, 50, text); drawingCanvas.addObject(textObj); } } } /** * 添加直线按钮的事件处理 */ @FXML public void onAddLine(ActionEvent actionEvent) { if (drawingCanvas == null) { showWarning("请先选择页面", "请先选择一个页面。"); return; } ShapeObject line = new ShapeObject(50, 50, 100, 100, ShapeObject.ShapeType.LINE); drawingCanvas.addObject(line); } /** * 添加矩形按钮的事件处理 */ @FXML public void onAddRectangle(ActionEvent actionEvent) { if (drawingCanvas == null) { showWarning("请先选择页面", "请先选择一个页面。"); return; } ShapeObject rect = new ShapeObject(50, 50, 100, 60, ShapeObject.ShapeType.RECTANGLE); drawingCanvas.addObject(rect); } /** * 添加圆形按钮的事件处理 */ @FXML public void onAddCircle(ActionEvent actionEvent) { if (drawingCanvas == null) { showWarning("请先选择页面", "请先选择一个页面。"); return; } ShapeObject circle = new ShapeObject(50, 50, 100, 100, ShapeObject.ShapeType.CIRCLE); drawingCanvas.addObject(circle); } /** * 添加椭圆按钮的事件处理 */ @FXML public void onAddEllipse(ActionEvent actionEvent) { if (drawingCanvas == null) { showWarning("请先选择页面", "请先选择一个页面。"); return; } ShapeObject ellipse = new ShapeObject(50, 50, 150, 80, ShapeObject.ShapeType.ELLIPSE); drawingCanvas.addObject(ellipse); } /** * 添加图片按钮的事件处理 */ @FXML public void onAddImage(ActionEvent actionEvent) { if (drawingCanvas == null) { showWarning("请先选择页面", "请先选择一个页面。"); return; } FileChooser fileChooser = new FileChooser(); fileChooser.setTitle("选择图片"); fileChooser.getExtensionFilters().addAll( new FileChooser.ExtensionFilter("图片文件", "*.png", "*.jpg", "*.jpeg", "*.gif", "*.bmp"), new FileChooser.ExtensionFilter("所有文件", "*.*") ); stage = (Stage) scenePane.getScene().getWindow(); File file = fileChooser.showOpenDialog(stage); if (file != null) { ImageObject imgObj = new ImageObject(50, 50, 200, 150, file.getAbsolutePath()); drawingCanvas.addObject(imgObj); } } /** * 退出按钮的事件处理 */ @FXML public void logout(ActionEvent actionEvent) { MyAlert alert = new MyAlert(Alert.AlertType.CONFIRMATION); alert.setTitle("退出"); alert.setHeaderText("您确定要退出吗?"); alert.setContentText("点击确定退出,点击取消返回。"); if (alert.showAndWait().orElse(null) == MyAlert.isOK()) { stage = (Stage) scenePane.getScene().getWindow(); stage.close(); } } /** * 打开幻灯片文件 */ @FXML public void onOpenSlideFile(ActionEvent actionEvent) { FileChooser fileChooser = new FileChooser(); fileChooser.setTitle("打开幻灯片"); fileChooser.getExtensionFilters().addAll( new FileChooser.ExtensionFilter("幻灯片文件 (*.hyperpoint)", "*.hyperpoint"), new FileChooser.ExtensionFilter("所有文件", "*.*") ); stage = (Stage) scenePane.getScene().getWindow(); File file = fileChooser.showOpenDialog(stage); if (file != null) { try { Presentation presentation = JsonSerializationUtil.loadFromJson(file); currentSlide = presentation.getSlides().isEmpty() ? null : presentation.getSlides().get(0); if (currentSlide != null) { loadSlidePages(); stage.setTitle("Hyperpoint - " + presentation.getName()); MyAlert alert = new MyAlert(Alert.AlertType.INFORMATION); alert.setTitle("成功"); alert.setHeaderText("幻灯片已打开"); alert.setContentText("幻灯片 \"" + presentation.getName() + "\" 已成功加载,包含 " + currentSlide.getPages().size() + " 个页面。"); alert.showAndWait(); } else { showWarning("加载失败", "幻灯片中没有找到任何页面。"); } } catch (Exception e) { showWarning("错误", "无法加载幻灯片文件: " + e.getMessage()); e.printStackTrace(); } } } /** * 保存幻灯片文件 */ @FXML public void onSaveSlide(ActionEvent actionEvent) { if (currentSlide == null) { showWarning("无幻灯片", "请先创建或打开一个幻灯片。"); return; } FileChooser fileChooser = new FileChooser(); fileChooser.setTitle("保存幻灯片"); fileChooser.getExtensionFilters().addAll( new FileChooser.ExtensionFilter("幻灯片文件", "*.hyperpoint"), new FileChooser.ExtensionFilter("所有文件", "*.*") ); fileChooser.setInitialFileName(currentSlide.getName() + ".hyperpoint"); stage = (Stage) scenePane.getScene().getWindow(); File file = fileChooser.showSaveDialog(stage); if (file != null) { try { // 创建临时演示文稿对象用于保存 Presentation tempPresentation = new Presentation(currentSlide.getName()); tempPresentation.addSlide(currentSlide); JsonSerializationUtil.saveToJson(tempPresentation, file); MyAlert alert = new MyAlert(Alert.AlertType.INFORMATION); alert.setTitle("成功"); alert.setHeaderText("幻灯片已保存"); alert.setContentText("幻灯片已保存到: " + file.getAbsolutePath()); alert.showAndWait(); } catch (Exception e) { showWarning("错误", "无法保存幻灯片文件: " + e.getMessage()); e.printStackTrace(); } } } /** * 导出页面为图片 */ @FXML public void onExportPage(ActionEvent actionEvent) { if (drawingCanvas == null) { showWarning("请先选择页面", "请先选择一个页面。"); return; } FileChooser fileChooser = new FileChooser(); fileChooser.setTitle("导出页面为图片"); fileChooser.getExtensionFilters().addAll( new FileChooser.ExtensionFilter("PNG图片 (*.png)", "*.png"), new FileChooser.ExtensionFilter("JPG图片 (*.jpg)", "*.jpg"), new FileChooser.ExtensionFilter("所有文件", "*.*") ); SlidePage currentPage = pageListView.getSelectionModel().getSelectedItem(); if (currentPage != null) { fileChooser.setInitialFileName(currentPage.getTitle() + ".png"); } stage = (Stage) scenePane.getScene().getWindow(); File file = fileChooser.showSaveDialog(stage); if (file != null) { try { String format = file.getName().endsWith(".jpg") || file.getName().endsWith(".jpeg") ? "jpg" : "png"; ExportUtil.exportPageAsImage(drawingCanvas, file, format); Alert alert = new Alert(Alert.AlertType.INFORMATION); alert.setTitle("成功"); alert.setHeaderText("页面已导出"); alert.setContentText("页面已导出到: " + file.getAbsolutePath()); alert.showAndWait(); } catch (Exception e) { showWarning("错误", "无法导出页面: " + e.getMessage()); e.printStackTrace(); } } } /** * 导出幻灯片为PDF */ @FXML public void onExportSlide(ActionEvent actionEvent) { if (currentSlide == null) { showWarning("无幻灯片", "请先创建或打开一个幻灯片。"); return; } FileChooser fileChooser = new FileChooser(); fileChooser.setTitle("导出幻灯片为PDF"); fileChooser.getExtensionFilters().addAll( new FileChooser.ExtensionFilter("PDF文件 (*.pdf)", "*.pdf"), new FileChooser.ExtensionFilter("所有文件", "*.*") ); fileChooser.setInitialFileName(currentSlide.getName() + ".pdf"); stage = (Stage) scenePane.getScene().getWindow(); File file = fileChooser.showSaveDialog(stage); if (file != null) { try { ExportUtil.exportSlideToPDF(currentSlide, file); Alert alert = new Alert(Alert.AlertType.INFORMATION); alert.setTitle("成功"); alert.setHeaderText("幻灯片已导出为PDF"); alert.setContentText("PDF已导出到: " + file.getAbsolutePath()); alert.showAndWait(); } catch (Exception e) { showWarning("错误", "无法导出PDF: " + e.getMessage()); e.printStackTrace(); } } } /** * 开始演示(默认淡出淡入效果) */ @FXML public void onStartPresentation(ActionEvent actionEvent) { if (currentSlide == null || currentSlide.getPages().isEmpty()) { showWarning("无幻灯片", "请先创建或打开一个幻灯片。"); return; } startPresentation(TransitionEffect.FADE); } /** * 使用淡出淡入效果播放演示 */ @FXML public void onPresentationWithFade(ActionEvent actionEvent) { if (currentSlide == null || currentSlide.getPages().isEmpty()) { showWarning("无幻灯片", "请先创建或打开一个幻灯片。"); return; } startPresentation(TransitionEffect.FADE); } /** * 使用从右推入效果播放演示 */ @FXML public void onPresentationWithPushLeft(ActionEvent actionEvent) { if (currentSlide == null || currentSlide.getPages().isEmpty()) { showWarning("无幻灯片", "请先创建或打开一个幻灯片。"); return; } startPresentation(TransitionEffect.PUSH_LEFT); } /** * 使用从左推入效果播放演示 */ @FXML public void onPresentationWithPushRight(ActionEvent actionEvent) { if (currentSlide == null || currentSlide.getPages().isEmpty()) { showWarning("无幻灯片", "请先创建或打开一个幻灯片。"); return; } startPresentation(TransitionEffect.PUSH_RIGHT); } /** * 使用缩放放大效果播放演示 */ @FXML public void onPresentationWithZoomIn(ActionEvent actionEvent) { if (currentSlide == null || currentSlide.getPages().isEmpty()) { showWarning("无幻灯片", "请先创建或打开一个幻灯片。"); return; } startPresentation(TransitionEffect.ZOOM_IN); } /** * 使用旋转翻页效果播放演示 */ @FXML public void onPresentationWithRotate(ActionEvent actionEvent) { if (currentSlide == null || currentSlide.getPages().isEmpty()) { showWarning("无幻灯片", "请先创建或打开一个幻灯片。"); return; } startPresentation(TransitionEffect.ROTATE); } /** * 启动演示 */ private void startPresentation(TransitionEffect transition) { PresentationWindow presentationWindow = new PresentationWindow(currentSlide, transition); presentationWindow.show(); } /** * 显示警告对话框 */ private void showWarning(String title, String message) { MyAlert alert = new MyAlert(Alert.AlertType.WARNING); alert.setTitle(title); alert.setHeaderText(title); alert.setContentText(message); alert.showAndWait(); } } " />
|
|
<option name="updatedContent" value="package dev.bytevibe.hyperpoint; import dev.bytevibe.hyperpoint.Utils.MyAlert; import dev.bytevibe.hyperpoint.Utils.MyTextInputDialog; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.*; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.VBox; import javafx.stage.FileChooser; import javafx.stage.Stage; import java.io.File; import java.net.URL; import java.util.ResourceBundle; public class Controller implements Initializable { @FXML private ListView<SlidePage> pageListView; @FXML private Label pageNameLabel; @FXML private AnchorPane drawingCanvasContainer; @FXML private VBox propertyPanelContainer; @FXML private AnchorPane scenePane; @FXML private MenuItem undoMenuItem; @FXML private MenuItem redoMenuItem; private Slide currentSlide; private DrawingCanvas drawingCanvas; private PropertyPanel propertyPanelComponent; private CommandHistory commandHistory; Stage stage; @Override public void initialize(URL url, ResourceBundle resourceBundle) { // 初始化命令历史 commandHistory = new CommandHistory(); commandHistory.setOnHistoryChanged(this::updateUndoRedoButtons); pageListView.setDisable(true); // 添加页面列表选择监听 pageListView.getSelectionModel().selectedItemProperty().addListener((obs, oldVal, newVal) -> { if (newVal != null) { displayPageContent(newVal); } }); } /** * 新建幻灯片按钮的事件处理 */ @FXML public void onNewSlide(ActionEvent actionEvent) { MyTextInputDialog dialog = new MyTextInputDialog("我的幻灯片"); dialog.setTitle("创建幻灯片"); dialog.setHeaderText("请输入幻灯片名称"); dialog.setContentText("幻灯片名称:"); if (dialog.showAndWait().isPresent()) { String slideName = dialog.getResult(); if (!slideName.trim().isEmpty()) { currentSlide = new Slide(slideName); loadSlidePages(); MyAlert alert = new MyAlert(Alert.AlertType.INFORMATION); alert.setTitle("成功"); alert.setHeaderText("幻灯片已创建"); alert.setContentText("幻灯片 \"" + slideName + "\" 已创建。"); stage = (Stage) scenePane.getScene().getWindow(); if (stage != null) { stage.setTitle("Hyperpoint - " + slideName); } alert.showAndWait(); } else { showWarning("无效名称", "幻灯片名称不能为空。"); } } } /** * 加载幻灯片的所有页面到列表中 */ private void loadSlidePages() { if (currentSlide != null) { pageListView.setItems(currentSlide.getPages()); pageListView.setDisable(false); if (!currentSlide.getPages().isEmpty()) { pageListView.getSelectionModel().selectFirst(); } } } /** * 显示页面内容 */ private void displayPageContent(SlidePage page) { if (page != null) { pageNameLabel.setText("页面名称: " + page.getTitle()); // 移除旧的Canvas drawingCanvasContainer.getChildren().clear(); propertyPanelContainer.getChildren().clear(); // 创建新的Canvas PageContent pageContent = page.getPageContent(); drawingCanvas = new DrawingCanvas(pageContent); drawingCanvas.setCommandHistory(commandHistory); // 设置命令历史 drawingCanvasContainer.getChildren().add(drawingCanvas); // 使用AnchorPane的约束来填充容器 AnchorPane.setTopAnchor(drawingCanvas, 0.0); AnchorPane.setLeftAnchor(drawingCanvas, 0.0); AnchorPane.setRightAnchor(drawingCanvas, 0.0); AnchorPane.setBottomAnchor(drawingCanvas, 0.0); // 创建属性面板 propertyPanelComponent = new PropertyPanel(drawingCanvas); propertyPanelContainer.getChildren().add(propertyPanelComponent); } } /** * 新建页面按钮的事件处理 */ @FXML public void onNewPage(ActionEvent actionEvent) { if (currentSlide == null) { showWarning("无幻灯片", "请先打开或创建一个幻灯片。"); return; } MyTextInputDialog dialog = new MyTextInputDialog("页面 " + (currentSlide.getPages().size() + 1)); dialog.setTitle("新建页面"); dialog.setHeaderText("请输入页面名称"); dialog.setContentText("页面名称:"); if (dialog.showAndWait().isPresent()) { String pageName = dialog.getResult(); if (!pageName.trim().isEmpty()) { SlidePage newPage = new SlidePage(pageName); currentSlide.addPage(newPage); pageListView.refresh(); pageListView.getSelectionModel().selectLast(); MyAlert alert = new MyAlert(Alert.AlertType.INFORMATION); alert.setTitle("成功"); alert.setHeaderText("页面已创建"); alert.setContentText("新页面 \"" + pageName + "\" 已添加。"); alert.showAndWait(); } } } /** * 删除页面按钮的事件处理 */ @FXML public void onDeletePage(ActionEvent actionEvent) { if (currentSlide == null || currentSlide.getPages().isEmpty()) { showWarning("无法删除", "没有可删除的页面。"); return; } int selectedIndex = pageListView.getSelectionModel().getSelectedIndex(); if (selectedIndex < 0) { showWarning("请选择页面", "请先选择要删除的页面。"); return; } Alert confirmAlert = new Alert(Alert.AlertType.CONFIRMATION); confirmAlert.setTitle("确认删除"); confirmAlert.setHeaderText("确认删除该页面?"); confirmAlert.setContentText("删除后无法恢复,确定继续吗?"); var result = confirmAlert.showAndWait(); if (result.isPresent() && result.get() == ButtonType.OK) { if (currentSlide.removePage(selectedIndex)) { pageListView.refresh(); if (selectedIndex >= currentSlide.getPages().size()) { pageListView.getSelectionModel().selectLast(); } else { pageListView.getSelectionModel().select(selectedIndex); } Alert alert = new Alert(Alert.AlertType.INFORMATION); alert.setTitle("成功"); alert.setHeaderText("页面已删除"); alert.showAndWait(); } else { showWarning("无法删除", "幻灯片至少需要保留一个页面。"); } } } /** * 添加文本按钮的事件处理 */ @FXML public void onAddText(ActionEvent actionEvent) { if (drawingCanvas == null) { showWarning("请先选择页面", "请先选择一个页面。"); return; } TextInputDialog dialog = new TextInputDialog("输入文本"); dialog.setTitle("添加文本"); dialog.setHeaderText("请输入要添加的文本"); dialog.setContentText("文本内容:"); if (dialog.showAndWait().isPresent()) { String text = dialog.getResult(); if (!text.trim().isEmpty()) { TextObject textObj = new TextObject(50, 50, text); // 使用命令历史记录此操作 SlidePage currentPage = pageListView.getSelectionModel().getSelectedItem(); if (currentPage != null) { AddObjectCommand command = new AddObjectCommand(currentPage.getPageContent(), textObj); commandHistory.execute(command); drawingCanvas.redraw(); } else { drawingCanvas.addObject(textObj); } } } } /** * 添加直线按钮的事件处理 */ @FXML public void onAddLine(ActionEvent actionEvent) { if (drawingCanvas == null) { showWarning("请先选择页面", "请先选择一个页面。"); return; } ShapeObject line = new ShapeObject(50, 50, 100, 100, ShapeObject.ShapeType.LINE); SlidePage currentPage = pageListView.getSelectionModel().getSelectedItem(); if (currentPage != null) { AddObjectCommand command = new AddObjectCommand(currentPage.getPageContent(), line); commandHistory.execute(command); drawingCanvas.redraw(); } else { drawingCanvas.addObject(line); } } /** * 添加矩形按钮的事件处理 */ @FXML public void onAddRectangle(ActionEvent actionEvent) { if (drawingCanvas == null) { showWarning("请先选择页面", "请先选择一个页面。"); return; } ShapeObject rect = new ShapeObject(50, 50, 100, 60, ShapeObject.ShapeType.RECTANGLE); SlidePage currentPage = pageListView.getSelectionModel().getSelectedItem(); if (currentPage != null) { AddObjectCommand command = new AddObjectCommand(currentPage.getPageContent(), rect); commandHistory.execute(command); drawingCanvas.redraw(); } else { drawingCanvas.addObject(rect); } } /** * 添加圆形按钮的事件处理 */ @FXML public void onAddCircle(ActionEvent actionEvent) { if (drawingCanvas == null) { showWarning("请先选择页面", "请先选择一个页面。"); return; } ShapeObject circle = new ShapeObject(50, 50, 100, 100, ShapeObject.ShapeType.CIRCLE); SlidePage currentPage = pageListView.getSelectionModel().getSelectedItem(); if (currentPage != null) { AddObjectCommand command = new AddObjectCommand(currentPage.getPageContent(), circle); commandHistory.execute(command); drawingCanvas.redraw(); } else { drawingCanvas.addObject(circle); } } /** * 添加椭圆按钮的事件处理 */ @FXML public void onAddEllipse(ActionEvent actionEvent) { if (drawingCanvas == null) { showWarning("请先选择页面", "请先选择一个页面。"); return; } ShapeObject ellipse = new ShapeObject(50, 50, 150, 80, ShapeObject.ShapeType.ELLIPSE); SlidePage currentPage = pageListView.getSelectionModel().getSelectedItem(); if (currentPage != null) { AddObjectCommand command = new AddObjectCommand(currentPage.getPageContent(), ellipse); commandHistory.execute(command); drawingCanvas.redraw(); } else { drawingCanvas.addObject(ellipse); } } /** * 添加图片按钮的事件处理 */ @FXML public void onAddImage(ActionEvent actionEvent) { if (drawingCanvas == null) { showWarning("请先选择页面", "请先选择一个页面。"); return; } FileChooser fileChooser = new FileChooser(); fileChooser.setTitle("选择图片"); fileChooser.getExtensionFilters().addAll( new FileChooser.ExtensionFilter("图片文件", "*.png", "*.jpg", "*.jpeg", "*.gif", "*.bmp"), new FileChooser.ExtensionFilter("所有文件", "*.*") ); stage = (Stage) scenePane.getScene().getWindow(); File file = fileChooser.showOpenDialog(stage); if (file != null) { ImageObject imgObj = new ImageObject(50, 50, 200, 150, file.getAbsolutePath()); SlidePage currentPage = pageListView.getSelectionModel().getSelectedItem(); if (currentPage != null) { AddObjectCommand command = new AddObjectCommand(currentPage.getPageContent(), imgObj); commandHistory.execute(command); drawingCanvas.redraw(); } else { drawingCanvas.addObject(imgObj); } } } /** * 退出按钮的事件处理 */ @FXML public void logout(ActionEvent actionEvent) { MyAlert alert = new MyAlert(Alert.AlertType.CONFIRMATION); alert.setTitle("退出"); alert.setHeaderText("您确定要退出吗?"); alert.setContentText("点击确定退出,点击取消返回。"); if (alert.showAndWait().orElse(null) == MyAlert.isOK()) { stage = (Stage) scenePane.getScene().getWindow(); stage.close(); } } /** * 打开幻灯片文件 */ @FXML public void onOpenSlideFile(ActionEvent actionEvent) { FileChooser fileChooser = new FileChooser(); fileChooser.setTitle("打开幻灯片"); fileChooser.getExtensionFilters().addAll( new FileChooser.ExtensionFilter("幻灯片文件 (*.hyperpoint)", "*.hyperpoint"), new FileChooser.ExtensionFilter("所有文件", "*.*") ); stage = (Stage) scenePane.getScene().getWindow(); File file = fileChooser.showOpenDialog(stage); if (file != null) { try { Presentation presentation = JsonSerializationUtil.loadFromJson(file); currentSlide = presentation.getSlides().isEmpty() ? null : presentation.getSlides().get(0); if (currentSlide != null) { loadSlidePages(); stage.setTitle("Hyperpoint - " + presentation.getName()); MyAlert alert = new MyAlert(Alert.AlertType.INFORMATION); alert.setTitle("成功"); alert.setHeaderText("幻灯片已打开"); alert.setContentText("幻灯片 \"" + presentation.getName() + "\" 已成功加载,包含 " + currentSlide.getPages().size() + " 个页面。"); alert.showAndWait(); } else { showWarning("加载失败", "幻灯片中没有找到任何页面。"); } } catch (Exception e) { showWarning("错误", "无法加载幻灯片文件: " + e.getMessage()); e.printStackTrace(); } } } /** * 保存幻灯片文件 */ @FXML public void onSaveSlide(ActionEvent actionEvent) { if (currentSlide == null) { showWarning("无幻灯片", "请先创建或打开一个幻灯片。"); return; } FileChooser fileChooser = new FileChooser(); fileChooser.setTitle("保存幻灯片"); fileChooser.getExtensionFilters().addAll( new FileChooser.ExtensionFilter("幻灯片文件", "*.hyperpoint"), new FileChooser.ExtensionFilter("所有文件", "*.*") ); fileChooser.setInitialFileName(currentSlide.getName() + ".hyperpoint"); stage = (Stage) scenePane.getScene().getWindow(); File file = fileChooser.showSaveDialog(stage); if (file != null) { try { // 创建临时演示文稿对象用于保存 Presentation tempPresentation = new Presentation(currentSlide.getName()); tempPresentation.addSlide(currentSlide); JsonSerializationUtil.saveToJson(tempPresentation, file); MyAlert alert = new MyAlert(Alert.AlertType.INFORMATION); alert.setTitle("成功"); alert.setHeaderText("幻灯片已保存"); alert.setContentText("幻灯片已保存到: " + file.getAbsolutePath()); alert.showAndWait(); } catch (Exception e) { showWarning("错误", "无法保存幻灯片文件: " + e.getMessage()); e.printStackTrace(); } } } /** * 导出页面为图片 */ @FXML public void onExportPage(ActionEvent actionEvent) { if (drawingCanvas == null) { showWarning("请先选择页面", "请先选择一个页面。"); return; } FileChooser fileChooser = new FileChooser(); fileChooser.setTitle("导出页面为图片"); fileChooser.getExtensionFilters().addAll( new FileChooser.ExtensionFilter("PNG图片 (*.png)", "*.png"), new FileChooser.ExtensionFilter("JPG图片 (*.jpg)", "*.jpg"), new FileChooser.ExtensionFilter("所有文件", "*.*") ); SlidePage currentPage = pageListView.getSelectionModel().getSelectedItem(); if (currentPage != null) { fileChooser.setInitialFileName(currentPage.getTitle() + ".png"); } stage = (Stage) scenePane.getScene().getWindow(); File file = fileChooser.showSaveDialog(stage); if (file != null) { try { String format = file.getName().endsWith(".jpg") || file.getName().endsWith(".jpeg") ? "jpg" : "png"; ExportUtil.exportPageAsImage(drawingCanvas, file, format); Alert alert = new Alert(Alert.AlertType.INFORMATION); alert.setTitle("成功"); alert.setHeaderText("页面已导出"); alert.setContentText("页面已导出到: " + file.getAbsolutePath()); alert.showAndWait(); } catch (Exception e) { showWarning("错误", "无法导出页面: " + e.getMessage()); e.printStackTrace(); } } } /** * 导出幻灯片为PDF */ @FXML public void onExportSlide(ActionEvent actionEvent) { if (currentSlide == null) { showWarning("无幻灯片", "请先创建或打开一个幻灯片。"); return; } FileChooser fileChooser = new FileChooser(); fileChooser.setTitle("导出幻灯片为PDF"); fileChooser.getExtensionFilters().addAll( new FileChooser.ExtensionFilter("PDF文件 (*.pdf)", "*.pdf"), new FileChooser.ExtensionFilter("所有文件", "*.*") ); fileChooser.setInitialFileName(currentSlide.getName() + ".pdf"); stage = (Stage) scenePane.getScene().getWindow(); File file = fileChooser.showSaveDialog(stage); if (file != null) { try { ExportUtil.exportSlideToPDF(currentSlide, file); Alert alert = new Alert(Alert.AlertType.INFORMATION); alert.setTitle("成功"); alert.setHeaderText("幻灯片已导出为PDF"); alert.setContentText("PDF已导出到: " + file.getAbsolutePath()); alert.showAndWait(); } catch (Exception e) { showWarning("错误", "无法导出PDF: " + e.getMessage()); e.printStackTrace(); } } } /** * 开始演示(默认淡出淡入效果) */ @FXML public void onStartPresentation(ActionEvent actionEvent) { if (currentSlide == null || currentSlide.getPages().isEmpty()) { showWarning("无幻灯片", "请先创建或打开一个幻灯片。"); return; } startPresentation(TransitionEffect.FADE); } /** * 使用淡出淡入效果播放演示 */ @FXML public void onPresentationWithFade(ActionEvent actionEvent) { if (currentSlide == null || currentSlide.getPages().isEmpty()) { showWarning("无幻灯片", "请先创建或打开一个幻灯片。"); return; } startPresentation(TransitionEffect.FADE); } /** * 使用从右推入效果播放演示 */ @FXML public void onPresentationWithPushLeft(ActionEvent actionEvent) { if (currentSlide == null || currentSlide.getPages().isEmpty()) { showWarning("无幻灯片", "请先创建或打开一个幻灯片。"); return; } startPresentation(TransitionEffect.PUSH_LEFT); } /** * 使用从左推入效果播放演示 */ @FXML public void onPresentationWithPushRight(ActionEvent actionEvent) { if (currentSlide == null || currentSlide.getPages().isEmpty()) { showWarning("无幻灯片", "请先创建或打开一个幻灯片。"); return; } startPresentation(TransitionEffect.PUSH_RIGHT); } /** * 使用缩放放大效果播放演示 */ @FXML public void onPresentationWithZoomIn(ActionEvent actionEvent) { if (currentSlide == null || currentSlide.getPages().isEmpty()) { showWarning("无幻灯片", "请先创建或打开一个幻灯片。"); return; } startPresentation(TransitionEffect.ZOOM_IN); } /** * 使用旋转翻页效果播放演示 */ @FXML public void onPresentationWithRotate(ActionEvent actionEvent) { if (currentSlide == null || currentSlide.getPages().isEmpty()) { showWarning("无幻灯片", "请先创建或打开一个幻灯片。"); return; } startPresentation(TransitionEffect.ROTATE); } /** * 启动演示 */ private void startPresentation(TransitionEffect transition) { PresentationWindow presentationWindow = new PresentationWindow(currentSlide, transition); presentationWindow.show(); } /** * 撤销操作 */ @FXML public void onUndo(ActionEvent actionEvent) { commandHistory.undo(); if (drawingCanvas != null) { drawingCanvas.redraw(); } } /** * 重做操作 */ @FXML public void onRedo(ActionEvent actionEvent) { commandHistory.redo(); if (drawingCanvas != null) { drawingCanvas.redraw(); } } /** * 更新撤销和重做按钮的状态 */ private void updateUndoRedoButtons() { // 确保菜单项不为null if (undoMenuItem != null) { undoMenuItem.setDisable(!commandHistory.canUndo()); } if (redoMenuItem != null) { redoMenuItem.setDisable(!commandHistory.canRedo()); } } /** * 获取命令历史 */ public CommandHistory getCommandHistory() { return commandHistory; } /** * 显示警告对话框 */ private void showWarning(String title, String message) { MyAlert alert = new MyAlert(Alert.AlertType.WARNING); alert.setTitle(title); alert.setHeaderText(title); alert.setContentText(message); alert.showAndWait(); } } " />
|
|
</PendingDiffInfo>
|
|
</value>
|
|
</entry>
|
|
<entry key="$PROJECT_DIR$/src/main/java/dev/bytevibe/hyperpoint/DeleteObjectCommand.java">
|
|
<value>
|
|
<PendingDiffInfo>
|
|
<option name="filePath" value="$PROJECT_DIR$/src/main/java/dev/bytevibe/hyperpoint/DeleteObjectCommand.java" />
|
|
<option name="updatedContent" value="package dev.bytevibe.hyperpoint; /** * 删除对象命令 */ public class DeleteObjectCommand implements Command { private PageContent pageContent; private DrawableObject object; private int originalIndex; public DeleteObjectCommand(PageContent pageContent, DrawableObject object) { this.pageContent = pageContent; this.object = object; this.originalIndex = pageContent.getObjectIndex(object); } @Override public void execute() { pageContent.removeObject(object); } @Override public void undo() { pageContent.addObjectAt(originalIndex, object); } } " />
|
|
</PendingDiffInfo>
|
|
</value>
|
|
</entry>
|
|
<entry key="$PROJECT_DIR$/src/main/java/dev/bytevibe/hyperpoint/DrawingCanvas.java">
|
|
<value>
|
|
<PendingDiffInfo>
|
|
<option name="filePath" value="$PROJECT_DIR$/src/main/java/dev/bytevibe/hyperpoint/DrawingCanvas.java" />
|
|
<option name="originalContent" value="package dev.bytevibe.hyperpoint; import javafx.scene.canvas.Canvas; import javafx.scene.canvas.GraphicsContext; import javafx.scene.image.Image; import javafx.scene.input.MouseEvent; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; import javafx.scene.text.Font; import javafx.scene.text.FontPosture; import javafx.scene.text.FontWeight; /** * 绘图Canvas组件,用于渲染和交互所有可绘制对象 */ public class DrawingCanvas extends Pane { // 页面标准尺寸(与全屏播放保持一致) public static final double PAGE_WIDTH = 1024; public static final double PAGE_HEIGHT = 768; // 缩放点的枚举 enum ResizePoint { TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT, NONE } private Canvas canvas; private PageContent pageContent; private DrawableObject selectedObject; private double lastX, lastY; private Runnable onSelectionChanged; // 缩放点有关的属性 private ResizePoint resizePoint = ResizePoint.NONE; // 缩放点大小 private static final double HANDLE_SIZE = 8; public DrawingCanvas(PageContent pageContent) { this.pageContent = pageContent; this.canvas = new Canvas(PAGE_WIDTH, PAGE_HEIGHT); getChildren().add(canvas); // 设置鼠标事件处理 setOnMousePressed(this::handleMousePressed); setOnMouseDragged(this::handleMouseDragged); setOnMouseReleased(this::handleMouseReleased); setStyle("-fx-border-color: #e0e0e0; -fx-border-width: 1;"); // 设置Pane的首选大小为页面尺寸,这样编辑界面显示时会按页面比例显示 setPrefWidth(PAGE_WIDTH); setPrefHeight(PAGE_HEIGHT); // 初始绘制 redraw(); } /** * 鼠标按下事件处理 */ private void handleMousePressed(MouseEvent event) { lastX = event.getX(); lastY = event.getY(); // 如果已经有选中对象,先检查是否点击在调整点上 if (selectedObject != null) { resizePoint = getResizePointAt(lastX, lastY, selectedObject); if (resizePoint != ResizePoint.NONE) { // 点击在调整点上,进入缩放模式 return; } } // 重置缩放模式 resizePoint = ResizePoint.NONE; // 查找被点击的对象 DrawableObject clicked = pageContent.findObjectAt(lastX, lastY); if (clicked == null) { setSelectedObject(null); } else { setSelectedObject(clicked); } redraw(); } /** * 获取点击位置对应的调整点 */ private ResizePoint getResizePointAt(double x, double y, DrawableObject obj) { double handleSize = HANDLE_SIZE; double objX = obj.getX(); double objY = obj.getY(); double objW = obj.getWidth(); double objH = obj.getHeight(); // 检查左上角 if (x >= objX - handleSize && x <= objX + handleSize && y >= objY - handleSize && y <= objY + handleSize) { return ResizePoint.TOP_LEFT; } // 检查右上角 if (x >= objX + objW - handleSize && x <= objX + objW + handleSize && y >= objY - handleSize && y <= objY + handleSize) { return ResizePoint.TOP_RIGHT; } // 检查左下角 if (x >= objX - handleSize && x <= objX + handleSize && y >= objY + objH - handleSize && y <= objY + objH + handleSize) { return ResizePoint.BOTTOM_LEFT; } // 检查右下角 if (x >= objX + objW - handleSize && x <= objX + objW + handleSize && y >= objY + objH - handleSize && y <= objY + objH + handleSize) { return ResizePoint.BOTTOM_RIGHT; } return ResizePoint.NONE; } /** * 鼠标拖动事件处理 */ private void handleMouseDragged(MouseEvent event) { if (selectedObject != null) { double dx = event.getX() - lastX; double dy = event.getY() - lastY; // 如果正在缩放 if (resizePoint != ResizePoint.NONE) { resizeObject(selectedObject, dx, dy, resizePoint); } else { // 移动对象 selectedObject.setX(selectedObject.getX() + dx); selectedObject.setY(selectedObject.getY() + dy); } lastX = event.getX(); lastY = event.getY(); redraw(); } } /** * 缩放对象 */ private void resizeObject(DrawableObject obj, double dx, double dy, ResizePoint point) { // 最小尺寸限制 final double MIN_SIZE = 20; switch (point) { case TOP_LEFT: // 从左上角缩放:x和y增加,width和height减少 if (obj.getWidth() - dx >= MIN_SIZE && obj.getHeight() - dy >= MIN_SIZE) { obj.setX(obj.getX() + dx); obj.setY(obj.getY() + dy); obj.setWidth(obj.getWidth() - dx); obj.setHeight(obj.getHeight() - dy); } break; case TOP_RIGHT: // 从右上角缩放:y增加,width增加,height减少 if (obj.getWidth() + dx >= MIN_SIZE && obj.getHeight() - dy >= MIN_SIZE) { obj.setY(obj.getY() + dy); obj.setWidth(obj.getWidth() + dx); obj.setHeight(obj.getHeight() - dy); } break; case BOTTOM_LEFT: // 从左下角缩放:x增加,width减少,height增加 if (obj.getWidth() - dx >= MIN_SIZE && obj.getHeight() + dy >= MIN_SIZE) { obj.setX(obj.getX() + dx); obj.setWidth(obj.getWidth() - dx); obj.setHeight(obj.getHeight() + dy); } break; case BOTTOM_RIGHT: // 从右下角缩放:width和height增加 if (obj.getWidth() + dx >= MIN_SIZE && obj.getHeight() + dy >= MIN_SIZE) { obj.setWidth(obj.getWidth() + dx); obj.setHeight(obj.getHeight() + dy); } break; default: break; } } /** * 鼠标释放事件处理 */ private void handleMouseReleased(MouseEvent event) { // 释放鼠标时,重置缩放模式 resizePoint = ResizePoint.NONE; } /** * 添加可绘制对象 */ public void addObject(DrawableObject object) { pageContent.addObject(object); redraw(); } /** * 移除可绘制对象 */ public void removeObject(DrawableObject object) { if (selectedObject == object) { selectedObject = null; } pageContent.removeObject(object); redraw(); } /** * 设置选中的对象 */ public void setSelectedObject(DrawableObject object) { selectedObject = object; if (onSelectionChanged != null) { onSelectionChanged.run(); } } /** * 获取选中的对象 */ public DrawableObject getSelectedObject() { return selectedObject; } /** * 设置选择变化回调 */ public void setOnSelectionChanged(Runnable callback) { this.onSelectionChanged = callback; } /** * 重新绘制画布 */ public void redraw() { GraphicsContext gc = canvas.getGraphicsContext2D(); // 清除画布(白色背景) gc.setFill(Color.WHITE); gc.fillRect(0, 0, canvas.getWidth(), canvas.getHeight()); // 绘制边框 gc.setStroke(Color.web("#cccccc")); gc.setLineWidth(1); gc.strokeRect(0, 0, canvas.getWidth(), canvas.getHeight()); // 绘制所有对象 for (DrawableObject obj : pageContent.getDrawableObjects()) { drawObject(gc, obj, obj == selectedObject); } } /** * 绘制单个对象 */ private void drawObject(GraphicsContext gc, DrawableObject obj, boolean selected) { if (obj instanceof TextObject) { drawTextObject(gc, (TextObject) obj, selected); } else if (obj instanceof ShapeObject) { drawShapeObject(gc, (ShapeObject) obj, selected); } else if (obj instanceof ImageObject) { drawImageObject(gc, (ImageObject) obj, selected); } } /** * 绘制文本对象 */ private void drawTextObject(GraphicsContext gc, TextObject textObj, boolean selected) { // 设置字体和风格 Font font = createFont(textObj.getFontFamily(), textObj.getFontSize(), textObj.getFontStyle()); gc.setFont(font); // 绘制背景 gc.setFill(Color.WHITE); gc.fillRect(textObj.getX(), textObj.getY(), textObj.getWidth(), textObj.getHeight()); gc.setStroke(Color.LIGHTGRAY); gc.strokeRect(textObj.getX(), textObj.getY(), textObj.getWidth(), textObj.getHeight()); // 设置文字颜色 gc.setFill(textObj.getTextColorAsColor()); // 获取排版后的文本行并绘制 double lineHeight = textObj.getFontSize() * 1.2; double currentY = textObj.getY() + 5; for (String line : textObj.getLayoutLines()) { currentY += lineHeight; gc.fillText(line, textObj.getX() + 5, currentY); } // 如果选中,绘制边框 if (selected) { drawSelectionBorder(gc, textObj); } } /** * 创建字体对象 */ private Font createFont(String family, double size, String style) { FontWeight weight = style.contains("BOLD") ? FontWeight.BOLD : FontWeight.NORMAL; FontPosture posture = style.contains("ITALIC") ? FontPosture.ITALIC : FontPosture.REGULAR; return Font.font(family, weight, posture, size); } /** * 绘制形状对象 */ private void drawShapeObject(GraphicsContext gc, ShapeObject shapeObj, boolean selected) { gc.setFill(shapeObj.getFillColorAsColor()); gc.setStroke(shapeObj.getStrokeColorAsColor()); gc.setLineWidth(shapeObj.getStrokeWidth()); switch (shapeObj.getShapeType()) { case RECTANGLE: gc.fillRect(shapeObj.getX(), shapeObj.getY(), shapeObj.getWidth(), shapeObj.getHeight()); gc.strokeRect(shapeObj.getX(), shapeObj.getY(), shapeObj.getWidth(), shapeObj.getHeight()); break; case CIRCLE: double radius = Math.min(shapeObj.getWidth(), shapeObj.getHeight()) / 2; double centerX = shapeObj.getX() + shapeObj.getWidth() / 2; double centerY = shapeObj.getY() + shapeObj.getHeight() / 2; gc.fillOval(centerX - radius, centerY - radius, radius * 2, radius * 2); gc.strokeOval(centerX - radius, centerY - radius, radius * 2, radius * 2); break; case ELLIPSE: gc.fillOval(shapeObj.getX(), shapeObj.getY(), shapeObj.getWidth(), shapeObj.getHeight()); gc.strokeOval(shapeObj.getX(), shapeObj.getY(), shapeObj.getWidth(), shapeObj.getHeight()); break; case LINE: gc.strokeLine(shapeObj.getX(), shapeObj.getY(), shapeObj.getX() + shapeObj.getWidth(), shapeObj.getY() + shapeObj.getHeight()); break; } // 如果选中,绘制边框 if (selected) { drawSelectionBorder(gc, shapeObj); } } /** * 绘制图片对象 */ private void drawImageObject(GraphicsContext gc, ImageObject imageObj, boolean selected) { try { Image image = new Image("file:" + imageObj.getImagePath()); gc.drawImage(image, imageObj.getX(), imageObj.getY(), imageObj.getWidth(), imageObj.getHeight()); } catch (Exception e) { // 如果图片加载失败,绘制占位符 gc.setFill(Color.LIGHTGRAY); gc.fillRect(imageObj.getX(), imageObj.getY(), imageObj.getWidth(), imageObj.getHeight()); gc.setFill(Color.BLACK); gc.fillText("[Image Not Found]", imageObj.getX() + 5, imageObj.getY() + 15); } // 如果选中,绘制边框 if (selected) { drawSelectionBorder(gc, imageObj); } } /** * 绘制选中对象的边框和句柄 */ private void drawSelectionBorder(GraphicsContext gc, DrawableObject obj) { gc.setStroke(Color.BLUE); gc.setLineWidth(2); gc.strokeRect(obj.getX() - 2, obj.getY() - 2, obj.getWidth() + 4, obj.getHeight() + 4); // 绘制8个调整点 drawResizeHandle(gc, obj.getX() - 4, obj.getY() - 4); // 左上 drawResizeHandle(gc, obj.getX() + obj.getWidth(), obj.getY() - 4); // 右上 drawResizeHandle(gc, obj.getX() - 4, obj.getY() + obj.getHeight()); // 左下 drawResizeHandle(gc, obj.getX() + obj.getWidth(), obj.getY() + obj.getHeight()); // 右下 } /** * 绘制调整点 */ private void drawResizeHandle(GraphicsContext gc, double x, double y) { gc.setFill(Color.BLUE); gc.fillRect(x, y, 8, 8); gc.setStroke(Color.WHITE); gc.setLineWidth(1); gc.strokeRect(x, y, 8, 8); } /** * 调整Canvas大小 */ public void resize(double width, double height) { canvas.setWidth(width); canvas.setHeight(height); setPrefSize(width, height); redraw(); } } " />
|
|
<option name="updatedContent" value="package dev.bytevibe.hyperpoint; import javafx.scene.canvas.Canvas; import javafx.scene.canvas.GraphicsContext; import javafx.scene.image.Image; import javafx.scene.input.MouseEvent; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; import javafx.scene.text.Font; import javafx.scene.text.FontPosture; import javafx.scene.text.FontWeight; /** * 绘图Canvas组件,用于渲染和交互所有可绘制对象 */ public class DrawingCanvas extends Pane { // 页面标准尺寸(与全屏播放保持一致) public static final double PAGE_WIDTH = 1024; public static final double PAGE_HEIGHT = 768; // 缩放点的枚举 enum ResizePoint { TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT, NONE } private Canvas canvas; private PageContent pageContent; private DrawableObject selectedObject; private double lastX, lastY; private Runnable onSelectionChanged; private CommandHistory commandHistory; // 缩放点有关的属性 private ResizePoint resizePoint = ResizePoint.NONE; // 缩放点大小 private static final double HANDLE_SIZE = 8; public DrawingCanvas(PageContent pageContent) { this.pageContent = pageContent; this.canvas = new Canvas(PAGE_WIDTH, PAGE_HEIGHT); getChildren().add(canvas); // 设置鼠标事件处理 setOnMousePressed(this::handleMousePressed); setOnMouseDragged(this::handleMouseDragged); setOnMouseReleased(this::handleMouseReleased); setStyle("-fx-border-color: #e0e0e0; -fx-border-width: 1;"); // 设置Pane的首选大小为页面尺寸,这样编辑界面显示时会按页面比例显示 setPrefWidth(PAGE_WIDTH); setPrefHeight(PAGE_HEIGHT); // 初始绘制 redraw(); } /** * 鼠标按下事件处理 */ private void handleMousePressed(MouseEvent event) { lastX = event.getX(); lastY = event.getY(); // 如果已经有选中对象,先检查是否点击在调整点上 if (selectedObject != null) { resizePoint = getResizePointAt(lastX, lastY, selectedObject); if (resizePoint != ResizePoint.NONE) { // 点击在调整点上,进入缩放模式 return; } } // 重置缩放模式 resizePoint = ResizePoint.NONE; // 查找被点击的对象 DrawableObject clicked = pageContent.findObjectAt(lastX, lastY); if (clicked == null) { setSelectedObject(null); } else { setSelectedObject(clicked); } redraw(); } /** * 获取点击位置对应的调整点 */ private ResizePoint getResizePointAt(double x, double y, DrawableObject obj) { double handleSize = HANDLE_SIZE; double objX = obj.getX(); double objY = obj.getY(); double objW = obj.getWidth(); double objH = obj.getHeight(); // 检查左上角 if (x >= objX - handleSize && x <= objX + handleSize && y >= objY - handleSize && y <= objY + handleSize) { return ResizePoint.TOP_LEFT; } // 检查右上角 if (x >= objX + objW - handleSize && x <= objX + objW + handleSize && y >= objY - handleSize && y <= objY + handleSize) { return ResizePoint.TOP_RIGHT; } // 检查左下角 if (x >= objX - handleSize && x <= objX + handleSize && y >= objY + objH - handleSize && y <= objY + objH + handleSize) { return ResizePoint.BOTTOM_LEFT; } // 检查右下角 if (x >= objX + objW - handleSize && x <= objX + objW + handleSize && y >= objY + objH - handleSize && y <= objY + objH + handleSize) { return ResizePoint.BOTTOM_RIGHT; } return ResizePoint.NONE; } /** * 鼠标拖动事件处理 */ private void handleMouseDragged(MouseEvent event) { if (selectedObject != null) { double dx = event.getX() - lastX; double dy = event.getY() - lastY; // 如果正在缩放 if (resizePoint != ResizePoint.NONE) { resizeObject(selectedObject, dx, dy, resizePoint); } else { // 移动对象 selectedObject.setX(selectedObject.getX() + dx); selectedObject.setY(selectedObject.getY() + dy); } lastX = event.getX(); lastY = event.getY(); redraw(); } } /** * 缩放对象 */ private void resizeObject(DrawableObject obj, double dx, double dy, ResizePoint point) { // 最小尺寸限制 final double MIN_SIZE = 20; switch (point) { case TOP_LEFT: // 从左上角缩放:x和y增加,width和height减少 if (obj.getWidth() - dx >= MIN_SIZE && obj.getHeight() - dy >= MIN_SIZE) { obj.setX(obj.getX() + dx); obj.setY(obj.getY() + dy); obj.setWidth(obj.getWidth() - dx); obj.setHeight(obj.getHeight() - dy); } break; case TOP_RIGHT: // 从右上角缩放:y增加,width增加,height减少 if (obj.getWidth() + dx >= MIN_SIZE && obj.getHeight() - dy >= MIN_SIZE) { obj.setY(obj.getY() + dy); obj.setWidth(obj.getWidth() + dx); obj.setHeight(obj.getHeight() - dy); } break; case BOTTOM_LEFT: // 从左下角缩放:x增加,width减少,height增加 if (obj.getWidth() - dx >= MIN_SIZE && obj.getHeight() + dy >= MIN_SIZE) { obj.setX(obj.getX() + dx); obj.setWidth(obj.getWidth() - dx); obj.setHeight(obj.getHeight() + dy); } break; case BOTTOM_RIGHT: // 从右下角缩放:width和height增加 if (obj.getWidth() + dx >= MIN_SIZE && obj.getHeight() + dy >= MIN_SIZE) { obj.setWidth(obj.getWidth() + dx); obj.setHeight(obj.getHeight() + dy); } break; default: break; } } /** * 鼠标释放事件处理 */ private void handleMouseReleased(MouseEvent event) { // 释放鼠标时,重置缩放模式 resizePoint = ResizePoint.NONE; } /** * 添加可绘制对象 */ public void addObject(DrawableObject object) { pageContent.addObject(object); redraw(); } /** * 移除可绘制对象 */ public void removeObject(DrawableObject object) { if (selectedObject == object) { selectedObject = null; } pageContent.removeObject(object); redraw(); } /** * 设置选中的对象 */ public void setSelectedObject(DrawableObject object) { selectedObject = object; if (onSelectionChanged != null) { onSelectionChanged.run(); } } /** * 获取选中的对象 */ public DrawableObject getSelectedObject() { return selectedObject; } /** * 设置选择变化回调 */ public void setOnSelectionChanged(Runnable callback) { this.onSelectionChanged = callback; } /** * 设置命令历史 */ public void setCommandHistory(CommandHistory commandHistory) { this.commandHistory = commandHistory; } /** * 重新绘制画布 */ public void redraw() { GraphicsContext gc = canvas.getGraphicsContext2D(); // 清除画布(白色背景) gc.setFill(Color.WHITE); gc.fillRect(0, 0, canvas.getWidth(), canvas.getHeight()); // 绘制边框 gc.setStroke(Color.web("#cccccc")); gc.setLineWidth(1); gc.strokeRect(0, 0, canvas.getWidth(), canvas.getHeight()); // 绘制所有对象 for (DrawableObject obj : pageContent.getDrawableObjects()) { drawObject(gc, obj, obj == selectedObject); } } /** * 绘制单个对象 */ private void drawObject(GraphicsContext gc, DrawableObject obj, boolean selected) { if (obj instanceof TextObject) { drawTextObject(gc, (TextObject) obj, selected); } else if (obj instanceof ShapeObject) { drawShapeObject(gc, (ShapeObject) obj, selected); } else if (obj instanceof ImageObject) { drawImageObject(gc, (ImageObject) obj, selected); } } /** * 绘制文本对象 */ private void drawTextObject(GraphicsContext gc, TextObject textObj, boolean selected) { // 设置字体和风格 Font font = createFont(textObj.getFontFamily(), textObj.getFontSize(), textObj.getFontStyle()); gc.setFont(font); // 绘制背景 gc.setFill(Color.WHITE); gc.fillRect(textObj.getX(), textObj.getY(), textObj.getWidth(), textObj.getHeight()); gc.setStroke(Color.LIGHTGRAY); gc.strokeRect(textObj.getX(), textObj.getY(), textObj.getWidth(), textObj.getHeight()); // 设置文字颜色 gc.setFill(textObj.getTextColorAsColor()); // 获取排版后的文本行并绘制 double lineHeight = textObj.getFontSize() * 1.2; double currentY = textObj.getY() + 5; for (String line : textObj.getLayoutLines()) { currentY += lineHeight; gc.fillText(line, textObj.getX() + 5, currentY); } // 如果选中,绘制边框 if (selected) { drawSelectionBorder(gc, textObj); } } /** * 创建字体对象 */ private Font createFont(String family, double size, String style) { FontWeight weight = style.contains("BOLD") ? FontWeight.BOLD : FontWeight.NORMAL; FontPosture posture = style.contains("ITALIC") ? FontPosture.ITALIC : FontPosture.REGULAR; return Font.font(family, weight, posture, size); } /** * 绘制形状对象 */ private void drawShapeObject(GraphicsContext gc, ShapeObject shapeObj, boolean selected) { gc.setFill(shapeObj.getFillColorAsColor()); gc.setStroke(shapeObj.getStrokeColorAsColor()); gc.setLineWidth(shapeObj.getStrokeWidth()); switch (shapeObj.getShapeType()) { case RECTANGLE: gc.fillRect(shapeObj.getX(), shapeObj.getY(), shapeObj.getWidth(), shapeObj.getHeight()); gc.strokeRect(shapeObj.getX(), shapeObj.getY(), shapeObj.getWidth(), shapeObj.getHeight()); break; case CIRCLE: double radius = Math.min(shapeObj.getWidth(), shapeObj.getHeight()) / 2; double centerX = shapeObj.getX() + shapeObj.getWidth() / 2; double centerY = shapeObj.getY() + shapeObj.getHeight() / 2; gc.fillOval(centerX - radius, centerY - radius, radius * 2, radius * 2); gc.strokeOval(centerX - radius, centerY - radius, radius * 2, radius * 2); break; case ELLIPSE: gc.fillOval(shapeObj.getX(), shapeObj.getY(), shapeObj.getWidth(), shapeObj.getHeight()); gc.strokeOval(shapeObj.getX(), shapeObj.getY(), shapeObj.getWidth(), shapeObj.getHeight()); break; case LINE: gc.strokeLine(shapeObj.getX(), shapeObj.getY(), shapeObj.getX() + shapeObj.getWidth(), shapeObj.getY() + shapeObj.getHeight()); break; } // 如果选中,绘制边框 if (selected) { drawSelectionBorder(gc, shapeObj); } } /** * 绘制图片对象 */ private void drawImageObject(GraphicsContext gc, ImageObject imageObj, boolean selected) { try { Image image = new Image("file:" + imageObj.getImagePath()); gc.drawImage(image, imageObj.getX(), imageObj.getY(), imageObj.getWidth(), imageObj.getHeight()); } catch (Exception e) { // 如果图片加载失败,绘制占位符 gc.setFill(Color.LIGHTGRAY); gc.fillRect(imageObj.getX(), imageObj.getY(), imageObj.getWidth(), imageObj.getHeight()); gc.setFill(Color.BLACK); gc.fillText("[Image Not Found]", imageObj.getX() + 5, imageObj.getY() + 15); } // 如果选中,绘制边框 if (selected) { drawSelectionBorder(gc, imageObj); } } /** * 绘制选中对象的边框和句柄 */ private void drawSelectionBorder(GraphicsContext gc, DrawableObject obj) { gc.setStroke(Color.BLUE); gc.setLineWidth(2); gc.strokeRect(obj.getX() - 2, obj.getY() - 2, obj.getWidth() + 4, obj.getHeight() + 4); // 绘制8个调整点 drawResizeHandle(gc, obj.getX() - 4, obj.getY() - 4); // 左上 drawResizeHandle(gc, obj.getX() + obj.getWidth(), obj.getY() - 4); // 右上 drawResizeHandle(gc, obj.getX() - 4, obj.getY() + obj.getHeight()); // 左下 drawResizeHandle(gc, obj.getX() + obj.getWidth(), obj.getY() + obj.getHeight()); // 右下 } /** * 绘制调整点 */ private void drawResizeHandle(GraphicsContext gc, double x, double y) { gc.setFill(Color.BLUE); gc.fillRect(x, y, 8, 8); gc.setStroke(Color.WHITE); gc.setLineWidth(1); gc.strokeRect(x, y, 8, 8); } /** * 调整Canvas大小 */ public void resize(double width, double height) { canvas.setWidth(width); canvas.setHeight(height); setPrefSize(width, height); redraw(); } }" />
|
|
</PendingDiffInfo>
|
|
</value>
|
|
</entry>
|
|
<entry key="$PROJECT_DIR$/src/main/java/dev/bytevibe/hyperpoint/ModifyObjectCommand.java">
|
|
<value>
|
|
<PendingDiffInfo>
|
|
<option name="filePath" value="$PROJECT_DIR$/src/main/java/dev/bytevibe/hyperpoint/ModifyObjectCommand.java" />
|
|
<option name="updatedContent" value="package dev.bytevibe.hyperpoint; /** * 修改对象命令 */ public class ModifyObjectCommand implements Command { private DrawableObject object; private double oldX, oldY, oldWidth, oldHeight; private double newX, newY, newWidth, newHeight; public ModifyObjectCommand(DrawableObject object, double oldX, double oldY, double oldWidth, double oldHeight, double newX, double newY, double newWidth, double newHeight) { this.object = object; this.oldX = oldX; this.oldY = oldY; this.oldWidth = oldWidth; this.oldHeight = oldHeight; this.newX = newX; this.newY = newY; this.newWidth = newWidth; this.newHeight = newHeight; } @Override public void execute() { object.setX(newX); object.setY(newY); object.setWidth(newWidth); object.setHeight(newHeight); } @Override public void undo() { object.setX(oldX); object.setY(oldY); object.setWidth(oldWidth); object.setHeight(oldHeight); } } " />
|
|
</PendingDiffInfo>
|
|
</value>
|
|
</entry>
|
|
<entry key="$PROJECT_DIR$/src/main/java/dev/bytevibe/hyperpoint/PageContent.java">
|
|
<value>
|
|
<PendingDiffInfo>
|
|
<option name="filePath" value="$PROJECT_DIR$/src/main/java/dev/bytevibe/hyperpoint/PageContent.java" />
|
|
<option name="originalContent" value="package dev.bytevibe.hyperpoint; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import java.io.Serializable; /** * 页面内容容器,用于保存页面上的所有可绘制对象 */ public class PageContent implements Serializable { private ObservableList<DrawableObject> drawableObjects; public PageContent() { this.drawableObjects = FXCollections.observableArrayList(); } public ObservableList<DrawableObject> getDrawableObjects() { return drawableObjects; } /** * 添加可绘制对象 */ public void addObject(DrawableObject object) { if (object != null) { drawableObjects.add(object); } } /** * 移除可绘制对象 */ public void removeObject(DrawableObject object) { drawableObjects.remove(object); } /** * 查找包含指定点的对象(从后到前,越后面的优先级越高) */ public DrawableObject findObjectAt(double x, double y) { for (int i = drawableObjects.size() - 1; i >= 0; i--) { DrawableObject obj = drawableObjects.get(i); if (obj.contains(x, y)) { return obj; } } return null; } /** * 获取对象列表大小 */ public int size() { return drawableObjects.size(); } /** * 清空所有对象 */ public void clear() { drawableObjects.clear(); } } " />
|
|
<option name="updatedContent" value="package dev.bytevibe.hyperpoint; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import java.io.Serializable; /** * 页面内容容器,用于保存页面上的所有可绘制对象 */ public class PageContent implements Serializable { private ObservableList<DrawableObject> drawableObjects; public PageContent() { this.drawableObjects = FXCollections.observableArrayList(); } public ObservableList<DrawableObject> getDrawableObjects() { return drawableObjects; } /** * 添加可绘制对象 */ public void addObject(DrawableObject object) { if (object != null) { drawableObjects.add(object); } } /** * 移除可绘制对象 */ public void removeObject(DrawableObject object) { drawableObjects.remove(object); } /** * 查找包含指定点的对象(从后到前,越后面的优先级越高) */ public DrawableObject findObjectAt(double x, double y) { for (int i = drawableObjects.size() - 1; i >= 0; i--) { DrawableObject obj = drawableObjects.get(i); if (obj.contains(x, y)) { return obj; } } return null; } /** * 获取对象列表大小 */ public int size() { return drawableObjects.size(); } /** * 清空所有对象 */ public void clear() { drawableObjects.clear(); } /** * 获取对象在列表中的索引 */ public int getObjectIndex(DrawableObject object) { return drawableObjects.indexOf(object); } /** * 在指定索引处添加对象 */ public void addObjectAt(int index, DrawableObject object) { if (object != null && index >= 0 && index <= drawableObjects.size()) { drawableObjects.add(index, object); } } }" />
|
|
</PendingDiffInfo>
|
|
</value>
|
|
</entry>
|
|
<entry key="$PROJECT_DIR$/src/main/resources/dev/bytevibe/hyperpoint/main.fxml">
|
|
<value>
|
|
<PendingDiffInfo>
|
|
<option name="filePath" value="$PROJECT_DIR$/src/main/resources/dev/bytevibe/hyperpoint/main.fxml" />
|
|
<option name="originalContent" value="<?xml version="1.0" encoding="UTF-8"?> <?import javafx.scene.control.Button?> <?import javafx.scene.control.Label?> <?import javafx.scene.control.ListView?> <?import javafx.scene.layout.AnchorPane?> <?import javafx.scene.layout.HBox?> <?import javafx.scene.layout.VBox?> <?import javafx.scene.control.MenuButton?> <?import javafx.scene.control.MenuItem?> <AnchorPane fx:id="scenePane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="700.0" prefWidth="1300.0" xmlns="http://javafx.com/javafx/25" xmlns:fx="http://javafx.com/fxml/1" fx:controller="dev.bytevibe.hyperpoint.Controller"> <children> <!-- 顶部菜单栏 --> <VBox layoutX="0" layoutY="0" prefHeight="50.0" prefWidth="1300.0" style="-fx-border-color: #cccccc; -fx-border-width: 0 0 1 0;"> <children> <HBox spacing="5" style="-fx-padding: 5;"> <children> <!-- 文件菜单 --> <MenuButton mnemonicParsing="false" text="文件"> <items> <MenuItem mnemonicParsing="false" onAction="#onNewSlide" text="新建幻灯片" /> <MenuItem mnemonicParsing="false" onAction="#onOpenSlideFile" text="打开幻灯片" /> <MenuItem mnemonicParsing="false" onAction="#onSaveSlide" text="保存幻灯片" /> <MenuItem mnemonicParsing="false" onAction="#onExportSlide" text="导出幻灯片(PDF)" /> </items> </MenuButton> <!-- 页面菜单 --> <MenuButton mnemonicParsing="false" text="页面"> <items> <MenuItem mnemonicParsing="false" onAction="#onNewPage" text="新建页面" /> <MenuItem mnemonicParsing="false" onAction="#onDeletePage" text="删除页面" /> <MenuItem mnemonicParsing="false" onAction="#onExportPage" text="导出页面(图片)" /> </items> </MenuButton> <!-- 插入菜单 --> <MenuButton mnemonicParsing="false" text="插入"> <items> <MenuItem mnemonicParsing="false" onAction="#onAddText" text="文本框" /> <MenuItem mnemonicParsing="false" onAction="#onAddLine" text="直线" /> <MenuItem mnemonicParsing="false" onAction="#onAddRectangle" text="矩形" /> <MenuItem mnemonicParsing="false" onAction="#onAddCircle" text="圆形" /> <MenuItem mnemonicParsing="false" onAction="#onAddEllipse" text="椭圆" /> <MenuItem mnemonicParsing="false" onAction="#onAddImage" text="图片" /> </items> </MenuButton> <!-- 演示菜单 --> <MenuButton mnemonicParsing="false" text="演示"> <items> <MenuItem mnemonicParsing="false" onAction="#onStartPresentation" text="开始演示(F5)" /> <MenuItem mnemonicParsing="false" onAction="#onPresentationWithFade" text="淡出淡入效果演示" /> <MenuItem mnemonicParsing="false" onAction="#onPresentationWithPushLeft" text="从右推入效果演示" /> <MenuItem mnemonicParsing="false" onAction="#onPresentationWithPushRight" text="从左推入效果演示" /> <MenuItem mnemonicParsing="false" onAction="#onPresentationWithZoomIn" text="缩放放大效果演示" /> <MenuItem mnemonicParsing="false" onAction="#onPresentationWithRotate" text="旋转翻页效果演示" /> </items> </MenuButton> <Label text=" | " /> <!-- 退出菜单 --> <MenuButton mnemonicParsing="false" text="更多"> <items> <MenuItem mnemonicParsing="false" onAction="#logout" text="退出应用" /> </items> </MenuButton> </children> </HBox> </children> </VBox> <!-- 主内容区域 --> <HBox layoutX="0" layoutY="50" prefHeight="652.0" prefWidth="1300.0"> <!-- 左侧页面列表 --> <VBox prefHeight="657.0" prefWidth="200.0" style="-fx-border-color: #e0e0e0; -fx-border-width: 0 1 0 0;"> <Label style="-fx-padding: 5; -fx-font-weight: bold;" text="页面列表" /> <ListView fx:id="pageListView" prefHeight="619.0" prefWidth="195.0" VBox.vgrow="ALWAYS" /> </VBox> <!-- 右侧编辑区域 --> <VBox spacing="10" style="-fx-padding: 10;" HBox.hgrow="ALWAYS"> <Label fx:id="pageNameLabel" style="-fx-font-size: 14; -fx-font-weight: bold;" text="页面名称:" /> <!-- 绘图Canvas将在这里动态添加 --> <AnchorPane fx:id="drawingCanvasContainer" HBox.hgrow="ALWAYS" VBox.vgrow="ALWAYS" /> </VBox> <!-- 右侧属性编辑面板 --> <VBox fx:id="propertyPanelContainer" prefWidth="200.0" /> </HBox> </children> </AnchorPane> " />
|
|
<option name="updatedContent" value="<?xml version="1.0" encoding="UTF-8"?> <?import javafx.scene.control.Button?> <?import javafx.scene.control.Label?> <?import javafx.scene.control.ListView?> <?import javafx.scene.layout.AnchorPane?> <?import javafx.scene.layout.HBox?> <?import javafx.scene.layout.VBox?> <?import javafx.scene.control.MenuButton?> <?import javafx.scene.control.MenuItem?> <AnchorPane fx:id="scenePane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="700.0" prefWidth="1300.0" xmlns="http://javafx.com/javafx/25" xmlns:fx="http://javafx.com/fxml/1" fx:controller="dev.bytevibe.hyperpoint.Controller"> <children> <!-- 顶部菜单栏 --> <VBox layoutX="0" layoutY="0" prefHeight="50.0" prefWidth="1300.0" style="-fx-border-color: #cccccc; -fx-border-width: 0 0 1 0;"> <children> <HBox spacing="5" style="-fx-padding: 5;"> <children> <!-- 文件菜单 --> <MenuButton mnemonicParsing="false" text="文件"> <items> <MenuItem mnemonicParsing="false" onAction="#onNewSlide" text="新建幻灯片" /> <MenuItem mnemonicParsing="false" onAction="#onOpenSlideFile" text="打开幻灯片" /> <MenuItem mnemonicParsing="false" onAction="#onSaveSlide" text="保存幻灯片" /> <MenuItem mnemonicParsing="false" onAction="#onExportSlide" text="导出幻灯片(PDF)" /> </items> </MenuButton> <!-- 编辑菜单 --> <MenuButton mnemonicParsing="false" text="编辑"> <items> <MenuItem fx:id="undoMenuItem" mnemonicParsing="false" onAction="#onUndo" text="撤销(Ctrl+Z)" /> <MenuItem fx:id="redoMenuItem" mnemonicParsing="false" onAction="#onRedo" text="重做(Ctrl+Y)" /> </items> </MenuButton> <!-- 页面菜单 --> <MenuButton mnemonicParsing="false" text="页面"> <items> <MenuItem mnemonicParsing="false" onAction="#onNewPage" text="新建页面" /> <MenuItem mnemonicParsing="false" onAction="#onDeletePage" text="删除页面" /> <MenuItem mnemonicParsing="false" onAction="#onExportPage" text="导出页面(图片)" /> </items> </MenuButton> <!-- 插入菜单 --> <MenuButton mnemonicParsing="false" text="插入"> <items> <MenuItem mnemonicParsing="false" onAction="#onAddText" text="文本框" /> <MenuItem mnemonicParsing="false" onAction="#onAddLine" text="直线" /> <MenuItem mnemonicParsing="false" onAction="#onAddRectangle" text="矩形" /> <MenuItem mnemonicParsing="false" onAction="#onAddCircle" text="圆形" /> <MenuItem mnemonicParsing="false" onAction="#onAddEllipse" text="椭圆" /> <MenuItem mnemonicParsing="false" onAction="#onAddImage" text="图片" /> </items> </MenuButton> <!-- 演示菜单 --> <MenuButton mnemonicParsing="false" text="演示"> <items> <MenuItem mnemonicParsing="false" onAction="#onStartPresentation" text="开始演示(F5)" /> <MenuItem mnemonicParsing="false" onAction="#onPresentationWithFade" text="淡出淡入效果演示" /> <MenuItem mnemonicParsing="false" onAction="#onPresentationWithPushLeft" text="从右推入效果演示" /> <MenuItem mnemonicParsing="false" onAction="#onPresentationWithPushRight" text="从左推入效果演示" /> <MenuItem mnemonicParsing="false" onAction="#onPresentationWithZoomIn" text="缩放放大效果演示" /> <MenuItem mnemonicParsing="false" onAction="#onPresentationWithRotate" text="旋转翻页效果演示" /> </items> </MenuButton> <Label text=" | " /> <!-- 退出菜单 --> <MenuButton mnemonicParsing="false" text="更多"> <items> <MenuItem mnemonicParsing="false" onAction="#logout" text="退出应用" /> </items> </MenuButton> </children> </HBox> </children> </VBox> <!-- 主内容区域 --> <HBox layoutX="0" layoutY="50" prefHeight="652.0" prefWidth="1300.0"> <!-- 左侧页面列表 --> <VBox prefHeight="657.0" prefWidth="200.0" style="-fx-border-color: #e0e0e0; -fx-border-width: 0 1 0 0;"> <Label style="-fx-padding: 5; -fx-font-weight: bold;" text="页面列表" /> <ListView fx:id="pageListView" prefHeight="619.0" prefWidth="195.0" VBox.vgrow="ALWAYS" /> </VBox> <!-- 右侧编辑区域 --> <VBox spacing="10" style="-fx-padding: 10;" HBox.hgrow="ALWAYS"> <Label fx:id="pageNameLabel" style="-fx-font-size: 14; -fx-font-weight: bold;" text="页面名称:" /> <!-- 绘图Canvas将在这里动态添加 --> <AnchorPane fx:id="drawingCanvasContainer" HBox.hgrow="ALWAYS" VBox.vgrow="ALWAYS" /> </VBox> <!-- 右侧属性编辑面板 --> <VBox fx:id="propertyPanelContainer" prefWidth="200.0" /> </HBox> </children> </AnchorPane> " />
|
|
</PendingDiffInfo>
|
|
</value>
|
|
</entry>
|
|
</map>
|
|
</option>
|
|
</component>
|
|
</project> |