From 02dd84c1f4143c6cdfaf24fb250ed69ac90ecaf0 Mon Sep 17 00:00:00 2001 From: Thibaud Date: Wed, 19 Feb 2025 13:44:23 +0100 Subject: [PATCH] handle multiple selection with shift --- .../java/ovh/gasser/newshapes/Selection.java | 29 +++++---- .../attributes/SelectionAttributes.java | 8 +++ .../newshapes/shapes/AbstractShape.java | 5 -- .../gasser/newshapes/shapes/SCollection.java | 6 +- .../ovh/gasser/newshapes/shapes/Shape.java | 1 - .../ovh/gasser/newshapes/ui/Controller.java | 59 +++++++++++++------ 6 files changed, 70 insertions(+), 38 deletions(-) diff --git a/src/main/java/ovh/gasser/newshapes/Selection.java b/src/main/java/ovh/gasser/newshapes/Selection.java index 0442859..a3f6915 100644 --- a/src/main/java/ovh/gasser/newshapes/Selection.java +++ b/src/main/java/ovh/gasser/newshapes/Selection.java @@ -2,23 +2,30 @@ package ovh.gasser.newshapes; import ovh.gasser.newshapes.attributes.SelectionAttributes; import ovh.gasser.newshapes.shapes.Shape; +import ovh.gasser.newshapes.util.Streamable; -public class Selection { +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; - private final SelectionAttributes attributes; - public final Shape shape; +public class Selection implements Streamable { + private final List selectedShapes = new ArrayList<>(); - public Selection(final Shape shape, boolean selected) { - this(shape); - attributes.selected = selected; + @Override + public Iterator iterator() { + return selectedShapes.iterator(); } - private Selection(final Shape shape) { - attributes = (SelectionAttributes) shape.getAttributes(SelectionAttributes.ID); - this.shape = shape; + public void clear() { + for (Shape shape : selectedShapes) { + shape.addAttributes(new SelectionAttributes(false)); + } + + selectedShapes.clear(); } - public void unselect() { - attributes.selected = false; + public void add(Shape s) { + selectedShapes.add(s); + s.addAttributes(new SelectionAttributes(true)); } } diff --git a/src/main/java/ovh/gasser/newshapes/attributes/SelectionAttributes.java b/src/main/java/ovh/gasser/newshapes/attributes/SelectionAttributes.java index f23383b..6cab6e9 100644 --- a/src/main/java/ovh/gasser/newshapes/attributes/SelectionAttributes.java +++ b/src/main/java/ovh/gasser/newshapes/attributes/SelectionAttributes.java @@ -4,6 +4,14 @@ public class SelectionAttributes implements Attributes { public static final String ID = "SELECTION_ATTRS"; public boolean selected; + public SelectionAttributes() { + this(false); + } + + public SelectionAttributes(boolean selected) { + this.selected = selected; + } + @Override public String getID() { return ID; diff --git a/src/main/java/ovh/gasser/newshapes/shapes/AbstractShape.java b/src/main/java/ovh/gasser/newshapes/shapes/AbstractShape.java index 4563fe9..7affa99 100644 --- a/src/main/java/ovh/gasser/newshapes/shapes/AbstractShape.java +++ b/src/main/java/ovh/gasser/newshapes/shapes/AbstractShape.java @@ -29,11 +29,6 @@ public abstract class AbstractShape implements Shape { attributes.put(attrs.getID(), attrs); } - @Override - public void setLoc(Point newLoc) { - getBounds().setLocation(newLoc); - } - @Override public void translate(int dx, int dy) { getBounds().translate(dx, dy); diff --git a/src/main/java/ovh/gasser/newshapes/shapes/SCollection.java b/src/main/java/ovh/gasser/newshapes/shapes/SCollection.java index 84c811a..17aef7c 100644 --- a/src/main/java/ovh/gasser/newshapes/shapes/SCollection.java +++ b/src/main/java/ovh/gasser/newshapes/shapes/SCollection.java @@ -12,7 +12,6 @@ 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); @@ -53,9 +52,8 @@ public class SCollection extends AbstractShape implements Streamable { } @Override - public void setLoc(Point newLoc) { - final Point loc = getBounds().getLocation(); - children.forEach(s -> s.translate(newLoc.x - loc.x, newLoc.y - loc.y)); + public void translate(int dx, int dy) { + children.forEach(s -> s.translate(dx, dy)); } @Override diff --git a/src/main/java/ovh/gasser/newshapes/shapes/Shape.java b/src/main/java/ovh/gasser/newshapes/shapes/Shape.java index 15a3fe9..59da208 100644 --- a/src/main/java/ovh/gasser/newshapes/shapes/Shape.java +++ b/src/main/java/ovh/gasser/newshapes/shapes/Shape.java @@ -7,7 +7,6 @@ import java.awt.*; public interface Shape { void accept(ShapeVisitor visitor); - void setLoc(Point newLoc); void translate(int dx, int dy); Attributes getAttributes(String key); void addAttributes(Attributes attr); diff --git a/src/main/java/ovh/gasser/newshapes/ui/Controller.java b/src/main/java/ovh/gasser/newshapes/ui/Controller.java index 4fe0c83..b1b7855 100644 --- a/src/main/java/ovh/gasser/newshapes/ui/Controller.java +++ b/src/main/java/ovh/gasser/newshapes/ui/Controller.java @@ -20,11 +20,13 @@ public class Controller { private final static Logger logger = LoggerFactory.getLogger(Controller.class); private final ShapesView view; private final SCollection model; - private Selection selection; + private final Selection selection; + private boolean shiftPressed; Controller(ShapesView view, SCollection model) { this.view = view; this.model = model; + this.selection = new Selection(); var adapter = new MouseAdapter() { @Override @@ -44,11 +46,20 @@ public class Controller { public void keyPressed(KeyEvent e) { handleKeyPressed(e); } + + @Override + public void keyReleased(KeyEvent e) { + handleKeyReleased(e); + } }); } private void handleMouseDragged(MouseEvent evt) { - if (selection != null) selection.shape.setLoc(evt.getPoint()); + for (Shape shape : selection) { + int dx = evt.getX() - shape.getBounds().x; + int dy = evt.getY() - shape.getBounds().y; + shape.translate(dx, dy); + } view.repaint(); } @@ -56,13 +67,13 @@ public class Controller { getTarget(evt, this.model) .ifPresentOrElse( s -> { - if (selection != null) resetSelection(); - selection = new Selection(s, true); - logger.debug("Selecting {}", selection.shape); + if (!shiftPressed) { + resetSelection(); + } + selection.add(s); + logger.debug("Selecting {}", s); }, - () -> { - if (selection != null) resetSelection(); - } + this::resetSelection ); view.repaint(); } @@ -73,10 +84,15 @@ public class Controller { case KeyEvent.VK_C -> copySelection(); case KeyEvent.VK_A -> changeSelectionColor(); case KeyEvent.VK_H -> exportHtml(); + case KeyEvent.VK_SHIFT -> shiftPressed = true; default -> logger.warn("Pressed unhandled key: {}", evt.getKeyChar()); } } + private void handleKeyReleased(KeyEvent evt) { + if (evt.getKeyCode() == KeyEvent.VK_SHIFT) shiftPressed = false; + } + private void exportHtml() { logger.info("Exporting view to html"); try { @@ -91,11 +107,15 @@ public class Controller { logger.debug("No selection to change color of"); return; } - if (selection.shape instanceof SCollection collection) { - collection.forEach(shape -> shape.addAttributes(new ColorAttributes(false, true, Color.BLACK, new Color((int) (Math.random() * 0x1000000))))); - } else { - selection.shape.addAttributes(new ColorAttributes(false, true, Color.BLACK, new Color((int) (Math.random() * 0x1000000)))); + + for (Shape s : selection) { + if (s instanceof SCollection collection) { + collection.forEach(shape -> shape.addAttributes(new ColorAttributes(false, true, Color.BLACK, new Color((int) (Math.random() * 0x1000000))))); + } else { + s.addAttributes(new ColorAttributes(false, true, Color.BLACK, new Color((int) (Math.random() * 0x1000000)))); + } } + view.repaint(); } @@ -104,22 +124,27 @@ public class Controller { logger.debug("No selection to copy"); return; } - this.model.add(selection.shape.clone()); + + for (Shape shape : selection) { + this.model.add(shape.clone()); + } + view.repaint(); } public void deleteSelected() { if (selection == null) return; logger.debug("Deleting selected shape(s)"); - this.model.remove(selection.shape); + for (Shape s : selection) { + this.model.remove(s); + } resetSelection(); view.repaint(); } private void resetSelection() { - logger.debug("Un-selecting {}", selection.shape); - selection.unselect(); - selection = null; + logger.debug("Resetting selection"); + selection.clear(); } private Optional getTarget(MouseEvent evt, SCollection sc) {