Files
hyperpoint/.idea/copilotDiffState.xml
T

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