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>
|
||||
<version>${javafx.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjfx</groupId>
|
||||
<artifactId>javafx-graphics</artifactId>
|
||||
<version>${javafx.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjfx</groupId>
|
||||
<artifactId>javafx-swing</artifactId>
|
||||
|
||||
@@ -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;
|
||||
@FXML
|
||||
private AnchorPane scenePane;
|
||||
@FXML
|
||||
private MenuItem undoMenuItem;
|
||||
@FXML
|
||||
private MenuItem redoMenuItem;
|
||||
|
||||
private Slide currentSlide;
|
||||
private DrawingCanvas drawingCanvas;
|
||||
private PropertyPanel propertyPanelComponent;
|
||||
private CommandHistory commandHistory;
|
||||
Stage stage;
|
||||
|
||||
@Override
|
||||
public void initialize(URL url, ResourceBundle resourceBundle) {
|
||||
// 初始化命令历史
|
||||
commandHistory = new CommandHistory();
|
||||
commandHistory.setOnHistoryChanged(this::updateUndoRedoButtons);
|
||||
|
||||
pageListView.setDisable(true);
|
||||
|
||||
// 添加页面列表选择监听
|
||||
@@ -103,6 +112,7 @@ public class Controller implements Initializable {
|
||||
// 创建新的Canvas
|
||||
PageContent pageContent = page.getPageContent();
|
||||
drawingCanvas = new DrawingCanvas(pageContent);
|
||||
drawingCanvas.setCommandHistory(commandHistory); // 设置命令历史
|
||||
drawingCanvasContainer.getChildren().add(drawingCanvas);
|
||||
|
||||
// 使用AnchorPane的约束来填充容器
|
||||
@@ -211,10 +221,18 @@ public class Controller implements Initializable {
|
||||
String text = dialog.getResult();
|
||||
if (!text.trim().isEmpty()) {
|
||||
TextObject textObj = new TextObject(50, 50, text);
|
||||
// 使用命令历史记录此操作
|
||||
SlidePage currentPage = pageListView.getSelectionModel().getSelectedItem();
|
||||
if (currentPage != null) {
|
||||
AddObjectCommand command = new AddObjectCommand(currentPage.getPageContent(), textObj);
|
||||
commandHistory.execute(command);
|
||||
drawingCanvas.redraw();
|
||||
} else {
|
||||
drawingCanvas.addObject(textObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加直线按钮的事件处理
|
||||
@@ -226,8 +244,15 @@ public class Controller implements Initializable {
|
||||
return;
|
||||
}
|
||||
ShapeObject line = new ShapeObject(50, 50, 100, 100, ShapeObject.ShapeType.LINE);
|
||||
SlidePage currentPage = pageListView.getSelectionModel().getSelectedItem();
|
||||
if (currentPage != null) {
|
||||
AddObjectCommand command = new AddObjectCommand(currentPage.getPageContent(), line);
|
||||
commandHistory.execute(command);
|
||||
drawingCanvas.redraw();
|
||||
} else {
|
||||
drawingCanvas.addObject(line);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加矩形按钮的事件处理
|
||||
@@ -239,8 +264,15 @@ public class Controller implements Initializable {
|
||||
return;
|
||||
}
|
||||
ShapeObject rect = new ShapeObject(50, 50, 100, 60, ShapeObject.ShapeType.RECTANGLE);
|
||||
SlidePage currentPage = pageListView.getSelectionModel().getSelectedItem();
|
||||
if (currentPage != null) {
|
||||
AddObjectCommand command = new AddObjectCommand(currentPage.getPageContent(), rect);
|
||||
commandHistory.execute(command);
|
||||
drawingCanvas.redraw();
|
||||
} else {
|
||||
drawingCanvas.addObject(rect);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加圆形按钮的事件处理
|
||||
@@ -252,8 +284,15 @@ public class Controller implements Initializable {
|
||||
return;
|
||||
}
|
||||
ShapeObject circle = new ShapeObject(50, 50, 100, 100, ShapeObject.ShapeType.CIRCLE);
|
||||
SlidePage currentPage = pageListView.getSelectionModel().getSelectedItem();
|
||||
if (currentPage != null) {
|
||||
AddObjectCommand command = new AddObjectCommand(currentPage.getPageContent(), circle);
|
||||
commandHistory.execute(command);
|
||||
drawingCanvas.redraw();
|
||||
} else {
|
||||
drawingCanvas.addObject(circle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加椭圆按钮的事件处理
|
||||
@@ -265,8 +304,15 @@ public class Controller implements Initializable {
|
||||
return;
|
||||
}
|
||||
ShapeObject ellipse = new ShapeObject(50, 50, 150, 80, ShapeObject.ShapeType.ELLIPSE);
|
||||
SlidePage currentPage = pageListView.getSelectionModel().getSelectedItem();
|
||||
if (currentPage != null) {
|
||||
AddObjectCommand command = new AddObjectCommand(currentPage.getPageContent(), ellipse);
|
||||
commandHistory.execute(command);
|
||||
drawingCanvas.redraw();
|
||||
} else {
|
||||
drawingCanvas.addObject(ellipse);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加图片按钮的事件处理
|
||||
@@ -290,9 +336,16 @@ public class Controller implements Initializable {
|
||||
|
||||
if (file != null) {
|
||||
ImageObject imgObj = new ImageObject(50, 50, 200, 150, file.getAbsolutePath());
|
||||
SlidePage currentPage = pageListView.getSelectionModel().getSelectedItem();
|
||||
if (currentPage != null) {
|
||||
AddObjectCommand command = new AddObjectCommand(currentPage.getPageContent(), imgObj);
|
||||
commandHistory.execute(command);
|
||||
drawingCanvas.redraw();
|
||||
} else {
|
||||
drawingCanvas.addObject(imgObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出按钮的事件处理
|
||||
@@ -550,6 +603,48 @@ public class Controller implements Initializable {
|
||||
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 double lastX, lastY;
|
||||
private Runnable onSelectionChanged;
|
||||
private CommandHistory commandHistory;
|
||||
|
||||
// 缩放点有关的属性
|
||||
private ResizePoint resizePoint = ResizePoint.NONE;
|
||||
@@ -241,6 +242,13 @@ public class DrawingCanvas extends Pane {
|
||||
this.onSelectionChanged = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置命令历史
|
||||
*/
|
||||
public void setCommandHistory(CommandHistory commandHistory) {
|
||||
this.commandHistory = commandHistory;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新绘制画布
|
||||
*/
|
||||
@@ -409,4 +417,3 @@ public class DrawingCanvas extends Pane {
|
||||
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() {
|
||||
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>
|
||||
</MenuButton>
|
||||
|
||||
<!-- 编辑菜单 -->
|
||||
<MenuButton mnemonicParsing="false" text="编辑">
|
||||
<items>
|
||||
<MenuItem fx:id="undoMenuItem" mnemonicParsing="false" onAction="#onUndo" text="撤销(Ctrl+Z)" />
|
||||
<MenuItem fx:id="redoMenuItem" mnemonicParsing="false" onAction="#onRedo" text="重做(Ctrl+Y)" />
|
||||
</items>
|
||||
</MenuButton>
|
||||
|
||||
<!-- 页面菜单 -->
|
||||
<MenuButton mnemonicParsing="false" text="页面">
|
||||
<items>
|
||||
|
||||
Reference in New Issue
Block a user