diff --git a/src/main/java/dev/bytevibe/hyperpoint/Controller.java b/src/main/java/dev/bytevibe/hyperpoint/Controller.java index 51cefa6..c0fc26b 100644 --- a/src/main/java/dev/bytevibe/hyperpoint/Controller.java +++ b/src/main/java/dev/bytevibe/hyperpoint/Controller.java @@ -136,6 +136,7 @@ public class Controller implements Initializable { // 创建新的Canvas PageContent pageContent = page.getPageContent(); drawingCanvas = new DrawingCanvas(pageContent); + drawingCanvas.setCurrentPage(page); // 设置当前页面 drawingCanvas.setCommandHistory(commandHistory); // 设置命令历史 drawingCanvasContainer.getChildren().add(drawingCanvas); diff --git a/src/main/java/dev/bytevibe/hyperpoint/DrawingCanvas.java b/src/main/java/dev/bytevibe/hyperpoint/DrawingCanvas.java index 7ee6766..4e307b3 100644 --- a/src/main/java/dev/bytevibe/hyperpoint/DrawingCanvas.java +++ b/src/main/java/dev/bytevibe/hyperpoint/DrawingCanvas.java @@ -29,6 +29,8 @@ public class DrawingCanvas extends Pane { private double lastX, lastY; private Runnable onSelectionChanged; private CommandHistory commandHistory; + private double startX, startY, startWidth, startHeight; + private SlidePage currentPage; // 缩放点有关的属性 private ResizePoint resizePoint = ResizePoint.NONE; @@ -65,12 +67,22 @@ public class DrawingCanvas extends Pane { // 如果已经有选中对象,先检查是否点击在调整点上 if (selectedObject != null) { - resizePoint = getResizePointAt(lastX, lastY, selectedObject); - if (resizePoint != ResizePoint.NONE) { - // 点击在调整点上,进入缩放模式 - return; - } + resizePoint = getResizePointAt(lastX, lastY, selectedObject); + if (resizePoint != ResizePoint.NONE) { + // 记录初始状态用于创建命令 + startX = selectedObject.getX(); + startY = selectedObject.getY(); + startWidth = selectedObject.getWidth(); + startHeight = selectedObject.getHeight(); + return; + } else if (isPointInObject(lastX, lastY, selectedObject)) { + // 记录移动前的位置 + startX = selectedObject.getX(); + startY = selectedObject.getY(); + startWidth = selectedObject.getWidth(); + startHeight = selectedObject.getHeight(); } + } // 重置缩放模式 resizePoint = ResizePoint.NONE; @@ -87,6 +99,14 @@ public class DrawingCanvas extends Pane { redraw(); } + /** + * 检查判断点是否在对象内 + */ + private boolean isPointInObject(double x, double y, DrawableObject obj) { + return x >= obj.getX() && x <= obj.getX() + obj.getWidth() && + y >= obj.getY() && y <= obj.getY() + obj.getHeight(); + } + /** * 获取点击位置对应的调整点 */ @@ -195,9 +215,23 @@ public class DrawingCanvas extends Pane { * 鼠标释放事件处理 */ private void handleMouseReleased(MouseEvent event) { - // 释放鼠标时,重置缩放模式 - resizePoint = ResizePoint.NONE; + // 如果有选中对象且位置或尺寸发生了变化,创建命令 + if (selectedObject != null && commandHistory != null) { + if (selectedObject.getX() != startX || selectedObject.getY() != startY || + selectedObject.getWidth() != startWidth || selectedObject.getHeight() != startHeight) { + + Command command = new ModifyObjectCommand( + selectedObject, + startX, startY, startWidth, startHeight, + selectedObject.getX(), selectedObject.getY(), + selectedObject.getWidth(), selectedObject.getHeight() + ); + commandHistory.execute(command); + } } + // 释放鼠标时,重置缩放模式 + resizePoint = ResizePoint.NONE; +} /** * 添加可绘制对象 @@ -249,6 +283,27 @@ public class DrawingCanvas extends Pane { this.commandHistory = commandHistory; } + /** + * 获取命令历史 + */ + public CommandHistory getCommandHistory() { + return commandHistory; + } + + /** + * 设置当前页面 + */ + public void setCurrentPage(SlidePage page) { + this.currentPage = page; + } + + /** + * 获取当前页面 + */ + public SlidePage getCurrentPage() { + return currentPage; + } + /** * 重新绘制画布 */ diff --git a/src/main/java/dev/bytevibe/hyperpoint/ModifyObjectPropertyCommand.java b/src/main/java/dev/bytevibe/hyperpoint/ModifyObjectPropertyCommand.java new file mode 100644 index 0000000..f9f66cb --- /dev/null +++ b/src/main/java/dev/bytevibe/hyperpoint/ModifyObjectPropertyCommand.java @@ -0,0 +1,124 @@ +package dev.bytevibe.hyperpoint; + +/** + * 修改对象属性命令(除位置和尺寸外的其他属性) + */ +public class ModifyObjectPropertyCommand implements Command { + private DrawableObject object; + private String oldText; + private String newText; + private String oldFontFamily; + private String newFontFamily; + private double oldFontSize; + private double newFontSize; + private String oldFontStyle; + private String newFontStyle; + private String oldTextColor; + private String newTextColor; + private String oldFillColor; + private String newFillColor; + private String oldStrokeColor; + private String newStrokeColor; + private double oldStrokeWidth; + private double newStrokeWidth; + private double oldRotation; + private double newRotation; + private PropertyType propertyType; + + // 定义属性类型枚举 + public enum PropertyType { + TEXT, FONT, COLOR, ROTATION, SHAPE_STYLE + } + + // 文本内容修改构造函数 + public ModifyObjectPropertyCommand(TextObject object, String oldText, String newText, int nothing) { + this.object = object; + this.oldText = oldText; + this.newText = newText; + this.propertyType = PropertyType.TEXT; + } + + // 字体样式修改构造函数 + public ModifyObjectPropertyCommand(TextObject object, + String oldFontFamily, String newFontFamily, + double oldFontSize, double newFontSize, + String oldFontStyle, String newFontStyle) { + this.object = object; + this.oldFontFamily = oldFontFamily; + this.newFontFamily = newFontFamily; + this.oldFontSize = oldFontSize; + this.newFontSize = newFontSize; + this.oldFontStyle = oldFontStyle; + this.newFontStyle = newFontStyle; + this.propertyType = PropertyType.FONT; + } + + // 文本颜色修改构造函数 + public ModifyObjectPropertyCommand(TextObject object, String oldTextColor, String newTextColor) { + this.object = object; + this.oldTextColor = oldTextColor; + this.newTextColor = newTextColor; + this.propertyType = PropertyType.COLOR; + } + + // 形状样式修改构造函数 + public ModifyObjectPropertyCommand(ShapeObject object, + String oldFillColor, String newFillColor, + String oldStrokeColor, String newStrokeColor, + double oldStrokeWidth, double newStrokeWidth) { + this.object = object; + this.oldFillColor = oldFillColor; + this.newFillColor = newFillColor; + this.oldStrokeColor = oldStrokeColor; + this.newStrokeColor = newStrokeColor; + this.oldStrokeWidth = oldStrokeWidth; + this.newStrokeWidth = newStrokeWidth; + this.propertyType = PropertyType.SHAPE_STYLE; + } + + // 旋转属性修改构造函数 + public ModifyObjectPropertyCommand(DrawableObject object, double oldRotation, double newRotation) { + this.object = object; + this.oldRotation = oldRotation; + this.newRotation = newRotation; + this.propertyType = PropertyType.ROTATION; + } + + @Override + public void execute() { + applyChanges(newText, newFontFamily, newFontSize, newFontStyle, + newTextColor, newFillColor, newStrokeColor, newStrokeWidth, newRotation); + } + + @Override + public void undo() { + applyChanges(oldText, oldFontFamily, oldFontSize, oldFontStyle, + oldTextColor, oldFillColor, oldStrokeColor, oldStrokeWidth, oldRotation); + } + + private void applyChanges(String text, String fontFamily, double fontSize, String fontStyle, + String textColor, String fillColor, String strokeColor, + double strokeWidth, double rotation) { + // 应用旋转(所有对象都有旋转属性) + object.setRotation(rotation); + + // 根据对象类型应用其他属性 + if (object instanceof TextObject) { + TextObject textObj = (TextObject) object; + if (propertyType == PropertyType.TEXT) { + textObj.setText(text); + } else if (propertyType == PropertyType.FONT) { + textObj.setFontFamily(fontFamily); + textObj.setFontSize(fontSize); + textObj.setFontStyle(fontStyle); + } else if (propertyType == PropertyType.COLOR) { + textObj.setTextColor(textColor); + } + } else if (object instanceof ShapeObject && propertyType == PropertyType.SHAPE_STYLE) { + ShapeObject shapeObj = (ShapeObject) object; + shapeObj.setFillColor(fillColor); + shapeObj.setStrokeColor(strokeColor); + shapeObj.setStrokeWidth(strokeWidth); + } + } +} \ No newline at end of file diff --git a/src/main/java/dev/bytevibe/hyperpoint/PropertyPanel.java b/src/main/java/dev/bytevibe/hyperpoint/PropertyPanel.java index faf818d..22301fc 100644 --- a/src/main/java/dev/bytevibe/hyperpoint/PropertyPanel.java +++ b/src/main/java/dev/bytevibe/hyperpoint/PropertyPanel.java @@ -175,17 +175,6 @@ public class PropertyPanel extends VBox implements Initializable { textContentField.setOnKeyReleased(e -> updateTextContent()); } - /** - * 更新旋转 - */ - private void updateRotation() { - DrawableObject selected = canvas.getSelectedObject(); - if (selected != null) { - selected.setRotation(rotationSlider.getValue()); - canvas.redraw(); - } - } - /** * 显示文本控件 */ @@ -256,40 +245,68 @@ public class PropertyPanel extends VBox implements Initializable { * 更新文本内容 */ private void updateTextContent() { - DrawableObject selected = canvas.getSelectedObject(); - if (selected instanceof TextObject) { - ((TextObject) selected).setText(textContentField.getText()); - canvas.redraw(); + DrawableObject selected = canvas.getSelectedObject(); + if (selected instanceof TextObject) { + TextObject textObj = (TextObject) selected; + String oldText = textObj.getText(); + String newText = textContentField.getText(); + + if (!oldText.equals(newText) && canvas.getCommandHistory() != null) { + Command command = new ModifyObjectPropertyCommand(textObj, oldText, newText, 0); + canvas.getCommandHistory().execute(command); } } +} /** * 更新文本风格 */ private void updateTextStyle() { - DrawableObject selected = canvas.getSelectedObject(); - if (selected instanceof TextObject) { - TextObject textObj = (TextObject) selected; - textObj.setFontFamily(fontFamilyCombo.getValue()); - textObj.setFontSize(fontSizeSpinner.getValue()); - textObj.setFontStyle(fontStyleCombo.getValue()); - canvas.redraw(); + DrawableObject selected = canvas.getSelectedObject(); + if (selected instanceof TextObject) { + TextObject textObj = (TextObject) selected; + String oldFontFamily = textObj.getFontFamily(); + double oldFontSize = textObj.getFontSize(); + String oldFontStyle = textObj.getFontStyle(); + + String newFontFamily = fontFamilyCombo.getValue(); + double newFontSize = fontSizeSpinner.getValue(); + String newFontStyle = fontStyleCombo.getValue(); + + if (!oldFontFamily.equals(newFontFamily) || oldFontSize != newFontSize || + !oldFontStyle.equals(newFontStyle) && canvas.getCommandHistory() != null) { + + Command command = new ModifyObjectPropertyCommand( + textObj, oldFontFamily, newFontFamily, + oldFontSize, newFontSize, + oldFontStyle, newFontStyle + ); + canvas.getCommandHistory().execute(command); } } +} + /** * 更新文本颜色 */ private void updateTextColor() { DrawableObject selected = canvas.getSelectedObject(); - if (selected instanceof TextObject) { + if (selected instanceof TextObject && canvas.getCommandHistory() != null) { + TextObject textObj = (TextObject) selected; + String oldColor = textObj.getTextColor(); + Color color = textColorPicker.getValue(); - String hexColor = String.format("%02X%02X%02X", + String newColor = String.format("%02X%02X%02X", (int) (color.getRed() * 255), (int) (color.getGreen() * 255), (int) (color.getBlue() * 255)); - ((TextObject) selected).setTextColor(hexColor); - canvas.redraw(); + + if (!oldColor.equals(newColor)) { + Command command = new ModifyObjectPropertyCommand(textObj, oldColor, newColor); + canvas.getCommandHistory().execute(command); + canvas.redraw(); + } } } @@ -298,14 +315,26 @@ public class PropertyPanel extends VBox implements Initializable { */ private void updateFillColor() { DrawableObject selected = canvas.getSelectedObject(); - if (selected instanceof ShapeObject) { + if (selected instanceof ShapeObject && canvas.getCommandHistory() != null) { + ShapeObject shapeObj = (ShapeObject) selected; + String oldFillColor = shapeObj.getFillColor(); + Color color = fillColorPicker.getValue(); - String hexColor = String.format("%02X%02X%02X", + String newFillColor = String.format("%02X%02X%02X", (int) (color.getRed() * 255), (int) (color.getGreen() * 255), (int) (color.getBlue() * 255)); - ((ShapeObject) selected).setFillColor(hexColor); - canvas.redraw(); + + if (!oldFillColor.equals(newFillColor)) { + Command command = new ModifyObjectPropertyCommand( + shapeObj, + oldFillColor, newFillColor, + shapeObj.getStrokeColor(), shapeObj.getStrokeColor(), + shapeObj.getStrokeWidth(), shapeObj.getStrokeWidth() + ); + canvas.getCommandHistory().execute(command); + canvas.redraw(); + } } } @@ -314,14 +343,26 @@ public class PropertyPanel extends VBox implements Initializable { */ private void updateStrokeColor() { DrawableObject selected = canvas.getSelectedObject(); - if (selected instanceof ShapeObject) { + if (selected instanceof ShapeObject && canvas.getCommandHistory() != null) { + ShapeObject shapeObj = (ShapeObject) selected; + String oldStrokeColor = shapeObj.getStrokeColor(); + Color color = strokeColorPicker.getValue(); - String hexColor = String.format("%02X%02X%02X", + String newStrokeColor = String.format("%02X%02X%02X", (int) (color.getRed() * 255), (int) (color.getGreen() * 255), (int) (color.getBlue() * 255)); - ((ShapeObject) selected).setStrokeColor(hexColor); - canvas.redraw(); + + if (!oldStrokeColor.equals(newStrokeColor)) { + Command command = new ModifyObjectPropertyCommand( + shapeObj, + shapeObj.getFillColor(), shapeObj.getFillColor(), + oldStrokeColor, newStrokeColor, + shapeObj.getStrokeWidth(), shapeObj.getStrokeWidth() + ); + canvas.getCommandHistory().execute(command); + canvas.redraw(); + } } } @@ -330,9 +371,38 @@ public class PropertyPanel extends VBox implements Initializable { */ private void updateStrokeWidth() { DrawableObject selected = canvas.getSelectedObject(); - if (selected instanceof ShapeObject) { - ((ShapeObject) selected).setStrokeWidth(strokeWidthSpinner.getValue()); - canvas.redraw(); + if (selected instanceof ShapeObject && canvas.getCommandHistory() != null) { + ShapeObject shapeObj = (ShapeObject) selected; + double oldWidth = shapeObj.getStrokeWidth(); + double newWidth = strokeWidthSpinner.getValue(); + + if (Math.abs(oldWidth - newWidth) > 0.01) { + Command command = new ModifyObjectPropertyCommand( + shapeObj, + shapeObj.getFillColor(), shapeObj.getFillColor(), + shapeObj.getStrokeColor(), shapeObj.getStrokeColor(), + oldWidth, newWidth + ); + canvas.getCommandHistory().execute(command); + canvas.redraw(); + } + } + } + + /** + * 更新旋转 + */ + private void updateRotation() { + DrawableObject selected = canvas.getSelectedObject(); + if (selected != null && canvas.getCommandHistory() != null) { + double oldRotation = selected.getRotation(); + double newRotation = rotationSlider.getValue(); + + if (Math.abs(oldRotation - newRotation) > 0.01) { + Command command = new ModifyObjectPropertyCommand(selected, oldRotation, newRotation); + canvas.getCommandHistory().execute(command); + canvas.redraw(); + } } } @@ -341,9 +411,15 @@ public class PropertyPanel extends VBox implements Initializable { */ private void deleteSelectedObject() { DrawableObject selected = canvas.getSelectedObject(); - if (selected != null) { - canvas.removeObject(selected); - updatePropertyPanel(); + if (selected != null && canvas.getCommandHistory() != null) { + SlidePage currentPage = canvas.getCurrentPage(); + if (currentPage != null) { + Command command = new DeleteObjectCommand(currentPage.getPageContent(), selected); + canvas.getCommandHistory().execute(command); + + canvas.setSelectedObject(null); + canvas.redraw(); + } } } }