feature:初步实现操作的撤回重做
This commit is contained in:
Generated
+94
File diff suppressed because one or more lines are too long
@@ -26,6 +26,11 @@
|
|||||||
<artifactId>javafx-fxml</artifactId>
|
<artifactId>javafx-fxml</artifactId>
|
||||||
<version>${javafx.version}</version>
|
<version>${javafx.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openjfx</groupId>
|
||||||
|
<artifactId>javafx-graphics</artifactId>
|
||||||
|
<version>${javafx.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.openjfx</groupId>
|
<groupId>org.openjfx</groupId>
|
||||||
<artifactId>javafx-swing</artifactId>
|
<artifactId>javafx-swing</artifactId>
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package dev.bytevibe.hyperpoint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加对象命令
|
||||||
|
*/
|
||||||
|
public class AddObjectCommand implements Command {
|
||||||
|
private PageContent pageContent;
|
||||||
|
private DrawableObject object;
|
||||||
|
|
||||||
|
public AddObjectCommand(PageContent pageContent, DrawableObject object) {
|
||||||
|
this.pageContent = pageContent;
|
||||||
|
this.object = object;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
pageContent.addObject(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void undo() {
|
||||||
|
pageContent.removeObject(object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package dev.bytevibe.hyperpoint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 命令接口,用于撤销和重做功能
|
||||||
|
*/
|
||||||
|
public interface Command {
|
||||||
|
/**
|
||||||
|
* 执行命令
|
||||||
|
*/
|
||||||
|
void execute();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 撤销命令
|
||||||
|
*/
|
||||||
|
void undo();
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package dev.bytevibe.hyperpoint;
|
||||||
|
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 命令历史管理器,支持撤销和重做
|
||||||
|
*/
|
||||||
|
public class CommandHistory {
|
||||||
|
private Stack<Command> undoStack;
|
||||||
|
private Stack<Command> redoStack;
|
||||||
|
private Runnable onHistoryChanged;
|
||||||
|
|
||||||
|
public CommandHistory() {
|
||||||
|
this.undoStack = new Stack<>();
|
||||||
|
this.redoStack = new Stack<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行命令并将其添加到历史记录
|
||||||
|
*/
|
||||||
|
public void execute(Command command) {
|
||||||
|
command.execute();
|
||||||
|
undoStack.push(command);
|
||||||
|
redoStack.clear(); // 执行新命令后,清空重做栈
|
||||||
|
notifyHistoryChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 撤销上一个命令
|
||||||
|
*/
|
||||||
|
public void undo() {
|
||||||
|
if (!undoStack.isEmpty()) {
|
||||||
|
Command command = undoStack.pop();
|
||||||
|
command.undo();
|
||||||
|
redoStack.push(command);
|
||||||
|
notifyHistoryChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重做上一个被撤销的命令
|
||||||
|
*/
|
||||||
|
public void redo() {
|
||||||
|
if (!redoStack.isEmpty()) {
|
||||||
|
Command command = redoStack.pop();
|
||||||
|
command.execute();
|
||||||
|
undoStack.push(command);
|
||||||
|
notifyHistoryChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否可以撤销
|
||||||
|
*/
|
||||||
|
public boolean canUndo() {
|
||||||
|
return !undoStack.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否可以重做
|
||||||
|
*/
|
||||||
|
public boolean canRedo() {
|
||||||
|
return !redoStack.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空历史记录
|
||||||
|
*/
|
||||||
|
public void clear() {
|
||||||
|
undoStack.clear();
|
||||||
|
redoStack.clear();
|
||||||
|
notifyHistoryChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置历史记录变化监听器
|
||||||
|
*/
|
||||||
|
public void setOnHistoryChanged(Runnable callback) {
|
||||||
|
this.onHistoryChanged = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通知历史记录已改变
|
||||||
|
*/
|
||||||
|
private void notifyHistoryChanged() {
|
||||||
|
if (onHistoryChanged != null) {
|
||||||
|
onHistoryChanged.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -26,14 +26,23 @@ public class Controller implements Initializable {
|
|||||||
private VBox propertyPanelContainer;
|
private VBox propertyPanelContainer;
|
||||||
@FXML
|
@FXML
|
||||||
private AnchorPane scenePane;
|
private AnchorPane scenePane;
|
||||||
|
@FXML
|
||||||
|
private MenuItem undoMenuItem;
|
||||||
|
@FXML
|
||||||
|
private MenuItem redoMenuItem;
|
||||||
|
|
||||||
private Slide currentSlide;
|
private Slide currentSlide;
|
||||||
private DrawingCanvas drawingCanvas;
|
private DrawingCanvas drawingCanvas;
|
||||||
private PropertyPanel propertyPanelComponent;
|
private PropertyPanel propertyPanelComponent;
|
||||||
|
private CommandHistory commandHistory;
|
||||||
Stage stage;
|
Stage stage;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize(URL url, ResourceBundle resourceBundle) {
|
public void initialize(URL url, ResourceBundle resourceBundle) {
|
||||||
|
// 初始化命令历史
|
||||||
|
commandHistory = new CommandHistory();
|
||||||
|
commandHistory.setOnHistoryChanged(this::updateUndoRedoButtons);
|
||||||
|
|
||||||
pageListView.setDisable(true);
|
pageListView.setDisable(true);
|
||||||
|
|
||||||
// 添加页面列表选择监听
|
// 添加页面列表选择监听
|
||||||
@@ -103,6 +112,7 @@ public class Controller implements Initializable {
|
|||||||
// 创建新的Canvas
|
// 创建新的Canvas
|
||||||
PageContent pageContent = page.getPageContent();
|
PageContent pageContent = page.getPageContent();
|
||||||
drawingCanvas = new DrawingCanvas(pageContent);
|
drawingCanvas = new DrawingCanvas(pageContent);
|
||||||
|
drawingCanvas.setCommandHistory(commandHistory); // 设置命令历史
|
||||||
drawingCanvasContainer.getChildren().add(drawingCanvas);
|
drawingCanvasContainer.getChildren().add(drawingCanvas);
|
||||||
|
|
||||||
// 使用AnchorPane的约束来填充容器
|
// 使用AnchorPane的约束来填充容器
|
||||||
@@ -211,10 +221,18 @@ public class Controller implements Initializable {
|
|||||||
String text = dialog.getResult();
|
String text = dialog.getResult();
|
||||||
if (!text.trim().isEmpty()) {
|
if (!text.trim().isEmpty()) {
|
||||||
TextObject textObj = new TextObject(50, 50, text);
|
TextObject textObj = new TextObject(50, 50, text);
|
||||||
|
// 使用命令历史记录此操作
|
||||||
|
SlidePage currentPage = pageListView.getSelectionModel().getSelectedItem();
|
||||||
|
if (currentPage != null) {
|
||||||
|
AddObjectCommand command = new AddObjectCommand(currentPage.getPageContent(), textObj);
|
||||||
|
commandHistory.execute(command);
|
||||||
|
drawingCanvas.redraw();
|
||||||
|
} else {
|
||||||
drawingCanvas.addObject(textObj);
|
drawingCanvas.addObject(textObj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加直线按钮的事件处理
|
* 添加直线按钮的事件处理
|
||||||
@@ -226,8 +244,15 @@ public class Controller implements Initializable {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ShapeObject line = new ShapeObject(50, 50, 100, 100, ShapeObject.ShapeType.LINE);
|
ShapeObject line = new ShapeObject(50, 50, 100, 100, ShapeObject.ShapeType.LINE);
|
||||||
|
SlidePage currentPage = pageListView.getSelectionModel().getSelectedItem();
|
||||||
|
if (currentPage != null) {
|
||||||
|
AddObjectCommand command = new AddObjectCommand(currentPage.getPageContent(), line);
|
||||||
|
commandHistory.execute(command);
|
||||||
|
drawingCanvas.redraw();
|
||||||
|
} else {
|
||||||
drawingCanvas.addObject(line);
|
drawingCanvas.addObject(line);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加矩形按钮的事件处理
|
* 添加矩形按钮的事件处理
|
||||||
@@ -239,8 +264,15 @@ public class Controller implements Initializable {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ShapeObject rect = new ShapeObject(50, 50, 100, 60, ShapeObject.ShapeType.RECTANGLE);
|
ShapeObject rect = new ShapeObject(50, 50, 100, 60, ShapeObject.ShapeType.RECTANGLE);
|
||||||
|
SlidePage currentPage = pageListView.getSelectionModel().getSelectedItem();
|
||||||
|
if (currentPage != null) {
|
||||||
|
AddObjectCommand command = new AddObjectCommand(currentPage.getPageContent(), rect);
|
||||||
|
commandHistory.execute(command);
|
||||||
|
drawingCanvas.redraw();
|
||||||
|
} else {
|
||||||
drawingCanvas.addObject(rect);
|
drawingCanvas.addObject(rect);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加圆形按钮的事件处理
|
* 添加圆形按钮的事件处理
|
||||||
@@ -252,8 +284,15 @@ public class Controller implements Initializable {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ShapeObject circle = new ShapeObject(50, 50, 100, 100, ShapeObject.ShapeType.CIRCLE);
|
ShapeObject circle = new ShapeObject(50, 50, 100, 100, ShapeObject.ShapeType.CIRCLE);
|
||||||
|
SlidePage currentPage = pageListView.getSelectionModel().getSelectedItem();
|
||||||
|
if (currentPage != null) {
|
||||||
|
AddObjectCommand command = new AddObjectCommand(currentPage.getPageContent(), circle);
|
||||||
|
commandHistory.execute(command);
|
||||||
|
drawingCanvas.redraw();
|
||||||
|
} else {
|
||||||
drawingCanvas.addObject(circle);
|
drawingCanvas.addObject(circle);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加椭圆按钮的事件处理
|
* 添加椭圆按钮的事件处理
|
||||||
@@ -265,8 +304,15 @@ public class Controller implements Initializable {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ShapeObject ellipse = new ShapeObject(50, 50, 150, 80, ShapeObject.ShapeType.ELLIPSE);
|
ShapeObject ellipse = new ShapeObject(50, 50, 150, 80, ShapeObject.ShapeType.ELLIPSE);
|
||||||
|
SlidePage currentPage = pageListView.getSelectionModel().getSelectedItem();
|
||||||
|
if (currentPage != null) {
|
||||||
|
AddObjectCommand command = new AddObjectCommand(currentPage.getPageContent(), ellipse);
|
||||||
|
commandHistory.execute(command);
|
||||||
|
drawingCanvas.redraw();
|
||||||
|
} else {
|
||||||
drawingCanvas.addObject(ellipse);
|
drawingCanvas.addObject(ellipse);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加图片按钮的事件处理
|
* 添加图片按钮的事件处理
|
||||||
@@ -290,9 +336,16 @@ public class Controller implements Initializable {
|
|||||||
|
|
||||||
if (file != null) {
|
if (file != null) {
|
||||||
ImageObject imgObj = new ImageObject(50, 50, 200, 150, file.getAbsolutePath());
|
ImageObject imgObj = new ImageObject(50, 50, 200, 150, file.getAbsolutePath());
|
||||||
|
SlidePage currentPage = pageListView.getSelectionModel().getSelectedItem();
|
||||||
|
if (currentPage != null) {
|
||||||
|
AddObjectCommand command = new AddObjectCommand(currentPage.getPageContent(), imgObj);
|
||||||
|
commandHistory.execute(command);
|
||||||
|
drawingCanvas.redraw();
|
||||||
|
} else {
|
||||||
drawingCanvas.addObject(imgObj);
|
drawingCanvas.addObject(imgObj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 退出按钮的事件处理
|
* 退出按钮的事件处理
|
||||||
@@ -550,6 +603,48 @@ public class Controller implements Initializable {
|
|||||||
presentationWindow.show();
|
presentationWindow.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 撤销操作
|
||||||
|
*/
|
||||||
|
@FXML
|
||||||
|
public void onUndo(ActionEvent actionEvent) {
|
||||||
|
commandHistory.undo();
|
||||||
|
if (drawingCanvas != null) {
|
||||||
|
drawingCanvas.redraw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重做操作
|
||||||
|
*/
|
||||||
|
@FXML
|
||||||
|
public void onRedo(ActionEvent actionEvent) {
|
||||||
|
commandHistory.redo();
|
||||||
|
if (drawingCanvas != null) {
|
||||||
|
drawingCanvas.redraw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新撤销和重做按钮的状态
|
||||||
|
*/
|
||||||
|
private void updateUndoRedoButtons() {
|
||||||
|
// 确保菜单项不为null
|
||||||
|
if (undoMenuItem != null) {
|
||||||
|
undoMenuItem.setDisable(!commandHistory.canUndo());
|
||||||
|
}
|
||||||
|
if (redoMenuItem != null) {
|
||||||
|
redoMenuItem.setDisable(!commandHistory.canRedo());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取命令历史
|
||||||
|
*/
|
||||||
|
public CommandHistory getCommandHistory() {
|
||||||
|
return commandHistory;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 显示警告对话框
|
* 显示警告对话框
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package dev.bytevibe.hyperpoint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除对象命令
|
||||||
|
*/
|
||||||
|
public class DeleteObjectCommand implements Command {
|
||||||
|
private PageContent pageContent;
|
||||||
|
private DrawableObject object;
|
||||||
|
private int originalIndex;
|
||||||
|
|
||||||
|
public DeleteObjectCommand(PageContent pageContent, DrawableObject object) {
|
||||||
|
this.pageContent = pageContent;
|
||||||
|
this.object = object;
|
||||||
|
this.originalIndex = pageContent.getObjectIndex(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
pageContent.removeObject(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void undo() {
|
||||||
|
pageContent.addObjectAt(originalIndex, object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -28,6 +28,7 @@ public class DrawingCanvas extends Pane {
|
|||||||
private DrawableObject selectedObject;
|
private DrawableObject selectedObject;
|
||||||
private double lastX, lastY;
|
private double lastX, lastY;
|
||||||
private Runnable onSelectionChanged;
|
private Runnable onSelectionChanged;
|
||||||
|
private CommandHistory commandHistory;
|
||||||
|
|
||||||
// 缩放点有关的属性
|
// 缩放点有关的属性
|
||||||
private ResizePoint resizePoint = ResizePoint.NONE;
|
private ResizePoint resizePoint = ResizePoint.NONE;
|
||||||
@@ -241,6 +242,13 @@ public class DrawingCanvas extends Pane {
|
|||||||
this.onSelectionChanged = callback;
|
this.onSelectionChanged = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置命令历史
|
||||||
|
*/
|
||||||
|
public void setCommandHistory(CommandHistory commandHistory) {
|
||||||
|
this.commandHistory = commandHistory;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 重新绘制画布
|
* 重新绘制画布
|
||||||
*/
|
*/
|
||||||
@@ -409,4 +417,3 @@ public class DrawingCanvas extends Pane {
|
|||||||
redraw();
|
redraw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package dev.bytevibe.hyperpoint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改对象命令
|
||||||
|
*/
|
||||||
|
public class ModifyObjectCommand implements Command {
|
||||||
|
private DrawableObject object;
|
||||||
|
private double oldX, oldY, oldWidth, oldHeight;
|
||||||
|
private double newX, newY, newWidth, newHeight;
|
||||||
|
|
||||||
|
public ModifyObjectCommand(DrawableObject object,
|
||||||
|
double oldX, double oldY, double oldWidth, double oldHeight,
|
||||||
|
double newX, double newY, double newWidth, double newHeight) {
|
||||||
|
this.object = object;
|
||||||
|
this.oldX = oldX;
|
||||||
|
this.oldY = oldY;
|
||||||
|
this.oldWidth = oldWidth;
|
||||||
|
this.oldHeight = oldHeight;
|
||||||
|
this.newX = newX;
|
||||||
|
this.newY = newY;
|
||||||
|
this.newWidth = newWidth;
|
||||||
|
this.newHeight = newHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
object.setX(newX);
|
||||||
|
object.setY(newY);
|
||||||
|
object.setWidth(newWidth);
|
||||||
|
object.setHeight(newHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void undo() {
|
||||||
|
object.setX(oldX);
|
||||||
|
object.setY(oldY);
|
||||||
|
object.setWidth(oldWidth);
|
||||||
|
object.setHeight(oldHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -61,5 +61,20 @@ public class PageContent implements Serializable {
|
|||||||
public void clear() {
|
public void clear() {
|
||||||
drawableObjects.clear();
|
drawableObjects.clear();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取对象在列表中的索引
|
||||||
|
*/
|
||||||
|
public int getObjectIndex(DrawableObject object) {
|
||||||
|
return drawableObjects.indexOf(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在指定索引处添加对象
|
||||||
|
*/
|
||||||
|
public void addObjectAt(int index, DrawableObject object) {
|
||||||
|
if (object != null && index >= 0 && index <= drawableObjects.size()) {
|
||||||
|
drawableObjects.add(index, object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -26,6 +26,14 @@
|
|||||||
</items>
|
</items>
|
||||||
</MenuButton>
|
</MenuButton>
|
||||||
|
|
||||||
|
<!-- 编辑菜单 -->
|
||||||
|
<MenuButton mnemonicParsing="false" text="编辑">
|
||||||
|
<items>
|
||||||
|
<MenuItem fx:id="undoMenuItem" mnemonicParsing="false" onAction="#onUndo" text="撤销(Ctrl+Z)" />
|
||||||
|
<MenuItem fx:id="redoMenuItem" mnemonicParsing="false" onAction="#onRedo" text="重做(Ctrl+Y)" />
|
||||||
|
</items>
|
||||||
|
</MenuButton>
|
||||||
|
|
||||||
<!-- 页面菜单 -->
|
<!-- 页面菜单 -->
|
||||||
<MenuButton mnemonicParsing="false" text="页面">
|
<MenuButton mnemonicParsing="false" text="页面">
|
||||||
<items>
|
<items>
|
||||||
|
|||||||
Reference in New Issue
Block a user