From aea90f5a93f4e682058440cf73a748f59c3b7933 Mon Sep 17 00:00:00 2001 From: Thibaud Date: Wed, 19 Feb 2025 11:27:45 +0100 Subject: [PATCH] Implement copy action --- src/main/java/ovh/gasser/newshapes/App.java | 3 +-- .../newshapes/shapes/AbstractShape.java | 3 +++ .../ovh/gasser/newshapes/shapes/SCircle.java | 12 ++++++++++ .../gasser/newshapes/shapes/SCollection.java | 13 ++++++++++ .../gasser/newshapes/shapes/SRectangle.java | 13 ++++++---- .../ovh/gasser/newshapes/shapes/Shape.java | 1 + .../ovh/gasser/newshapes/ui/Controller.java | 24 +++++++++++++------ .../ovh/gasser/newshapes/ui/ShapesView.java | 3 ++- 8 files changed, 58 insertions(+), 14 deletions(-) diff --git a/src/main/java/ovh/gasser/newshapes/App.java b/src/main/java/ovh/gasser/newshapes/App.java index 2f0ba10..76867d7 100644 --- a/src/main/java/ovh/gasser/newshapes/App.java +++ b/src/main/java/ovh/gasser/newshapes/App.java @@ -3,7 +3,6 @@ package ovh.gasser.newshapes; import ovh.gasser.newshapes.shapes.SCircle; import ovh.gasser.newshapes.shapes.SCollection; import ovh.gasser.newshapes.shapes.SRectangle; -import ovh.gasser.newshapes.shapes.Shape; import ovh.gasser.newshapes.ui.ShapesView; import javax.swing.*; @@ -12,7 +11,7 @@ import java.awt.*; public class App { public static final Dimension WIN_SIZE = new Dimension(800, 600); - private Shape model; + private SCollection model; private App() throws HeadlessException { final JFrame frame = new JFrame("Reactive shapes"); diff --git a/src/main/java/ovh/gasser/newshapes/shapes/AbstractShape.java b/src/main/java/ovh/gasser/newshapes/shapes/AbstractShape.java index 2515878..4563fe9 100644 --- a/src/main/java/ovh/gasser/newshapes/shapes/AbstractShape.java +++ b/src/main/java/ovh/gasser/newshapes/shapes/AbstractShape.java @@ -48,4 +48,7 @@ public abstract class AbstractShape implements Shape { public String toString() { return String.format("x=%d, y=%d, width=%d, height=%d", bounds.x, bounds.y, bounds.width, bounds.height); } + + @Override + public abstract Shape clone(); } diff --git a/src/main/java/ovh/gasser/newshapes/shapes/SCircle.java b/src/main/java/ovh/gasser/newshapes/shapes/SCircle.java index c910c34..51b05c0 100644 --- a/src/main/java/ovh/gasser/newshapes/shapes/SCircle.java +++ b/src/main/java/ovh/gasser/newshapes/shapes/SCircle.java @@ -1,6 +1,7 @@ package ovh.gasser.newshapes.shapes; import ovh.gasser.newshapes.ShapeVisitor; +import ovh.gasser.newshapes.attributes.ColorAttributes; import ovh.gasser.newshapes.attributes.SelectionAttributes; import java.awt.*; @@ -19,13 +20,24 @@ public class SCircle extends AbstractShape { visitor.visitCircle(this); } + @Override + public Shape clone() { + var color = (ColorAttributes) getAttributes(ColorAttributes.ID); + return SCircle.create(super.getBounds().x, super.getBounds().y, this.radius, color.strokedColor); + } + public int getRadius() { return radius; } public static SCircle create(int x, int y, int radius) { + return create(x, y, radius, Color.BLACK); + } + + public static SCircle create(int x, int y, int radius, Color color) { var circle = new SCircle(x, y, radius); circle.addAttributes(new SelectionAttributes()); + circle.addAttributes(new ColorAttributes(false, true, Color.BLACK, color)); return circle; } } diff --git a/src/main/java/ovh/gasser/newshapes/shapes/SCollection.java b/src/main/java/ovh/gasser/newshapes/shapes/SCollection.java index f5ffdbb..f945eb7 100644 --- a/src/main/java/ovh/gasser/newshapes/shapes/SCollection.java +++ b/src/main/java/ovh/gasser/newshapes/shapes/SCollection.java @@ -12,6 +12,7 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Spliterator; +import java.util.stream.Collectors; public class SCollection extends AbstractShape implements Streamable { private final static Logger logger = LoggerFactory.getLogger(SCollection.class); @@ -43,6 +44,14 @@ public class SCollection extends AbstractShape implements Streamable { } } + @Override + public Shape clone() { + var clonedChildren = children.stream().map(Shape::clone).toArray(Shape[]::new); + var collection = new SCollection(clonedChildren); + collection.addAttributes(new SelectionAttributes()); + return collection; + } + @Override public void setLoc(Point newLoc) { final Point loc = getBounds().getLocation(); @@ -59,6 +68,10 @@ public class SCollection extends AbstractShape implements Streamable { return children.spliterator(); } + public void add(Shape s) { + children.add(s); + } + public void remove(Shape s) { if (!children.remove(s)) { logger.error("Unable to delete shape: {}", s); diff --git a/src/main/java/ovh/gasser/newshapes/shapes/SRectangle.java b/src/main/java/ovh/gasser/newshapes/shapes/SRectangle.java index f088e72..9f868d4 100644 --- a/src/main/java/ovh/gasser/newshapes/shapes/SRectangle.java +++ b/src/main/java/ovh/gasser/newshapes/shapes/SRectangle.java @@ -24,14 +24,19 @@ public class SRectangle extends AbstractShape { '}'; } + @Override + public Shape clone() { + var color = (ColorAttributes) this.getAttributes(ColorAttributes.ID); + return SRectangle.create(super.getBounds().x, super.getBounds().y, getBounds().width, getBounds().height, color.strokedColor); + } + public static SRectangle create(int x, int y, int width, int height) { - SRectangle rect = new SRectangle(new Rectangle(x, y, width, height)); - rect.addAttributes(new SelectionAttributes()); - return rect; + return create(x, y, width, height, Color.BLACK); } public static SRectangle create(int x, int y, int width, int height, Color color) { - final SRectangle rect = create(x, y, width, height); + SRectangle rect = new SRectangle(new Rectangle(x, y, width, height)); + rect.addAttributes(new SelectionAttributes()); rect.addAttributes(new ColorAttributes(false, true, Color.BLACK, color)); return rect; } diff --git a/src/main/java/ovh/gasser/newshapes/shapes/Shape.java b/src/main/java/ovh/gasser/newshapes/shapes/Shape.java index d556fac..15a3fe9 100644 --- a/src/main/java/ovh/gasser/newshapes/shapes/Shape.java +++ b/src/main/java/ovh/gasser/newshapes/shapes/Shape.java @@ -12,4 +12,5 @@ public interface Shape { Attributes getAttributes(String key); void addAttributes(Attributes attr); Rectangle getBounds(); + Shape clone(); } diff --git a/src/main/java/ovh/gasser/newshapes/ui/Controller.java b/src/main/java/ovh/gasser/newshapes/ui/Controller.java index 70a38dd..6c49c51 100644 --- a/src/main/java/ovh/gasser/newshapes/ui/Controller.java +++ b/src/main/java/ovh/gasser/newshapes/ui/Controller.java @@ -15,10 +15,10 @@ import java.util.Optional; public class Controller { private final static Logger logger = LoggerFactory.getLogger(Controller.class); private final ShapesView view; - private final Shape model; + private final SCollection model; private Selection selection; - Controller(ShapesView view, Shape model) { + Controller(ShapesView view, SCollection model) { this.view = view; this.model = model; @@ -53,8 +53,7 @@ public class Controller { } private void handleMousePressed(MouseEvent evt) { - var sc = (SCollection) this.model; - getTarget(evt, sc) + getTarget(evt, this.model) .ifPresentOrElse( s -> { if (selection != null) resetSelection(); @@ -73,18 +72,29 @@ public class Controller { case KeyEvent.VK_DELETE: deleteSelected(); break; + case KeyEvent.VK_C: + copySelection(); + break; default: logger.warn("Pressed unhandled key: {}", evt.getKeyChar()); break; } } + private void copySelection() { + if (selection == null) { + logger.debug("No selection to copy"); + return; + } + this.model.add(selection.shape.clone()); + view.repaint(); + } + private void deleteSelected() { if (selection == null) return; logger.debug("Deleting selected shape(s)"); - var sc = (SCollection) this.model; - sc.remove(selection.shape); - selection = null; + this.model.remove(selection.shape); + resetSelection(); view.repaint(); } diff --git a/src/main/java/ovh/gasser/newshapes/ui/ShapesView.java b/src/main/java/ovh/gasser/newshapes/ui/ShapesView.java index 110d83e..9d18480 100644 --- a/src/main/java/ovh/gasser/newshapes/ui/ShapesView.java +++ b/src/main/java/ovh/gasser/newshapes/ui/ShapesView.java @@ -3,6 +3,7 @@ package ovh.gasser.newshapes.ui; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ovh.gasser.newshapes.ShapeVisitor; +import ovh.gasser.newshapes.shapes.SCollection; import ovh.gasser.newshapes.shapes.Shape; import javax.swing.*; @@ -15,7 +16,7 @@ public class ShapesView extends JPanel { private final Controller controller; private ShapeVisitor draftman; - public ShapesView(Shape model) { + public ShapesView(SCollection model) { this.model = model; this.controller = new Controller(this, model); }