sync edit menu checkbox state with the selected shape
This commit is contained in:
@@ -2,21 +2,27 @@ package ovh.gasser.newshapes;
|
|||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import ovh.gasser.newshapes.attributes.ColorAttributes;
|
||||||
import ovh.gasser.newshapes.shapes.SCircle;
|
import ovh.gasser.newshapes.shapes.SCircle;
|
||||||
import ovh.gasser.newshapes.shapes.SCollection;
|
import ovh.gasser.newshapes.shapes.SCollection;
|
||||||
import ovh.gasser.newshapes.shapes.SRectangle;
|
import ovh.gasser.newshapes.shapes.SRectangle;
|
||||||
|
import ovh.gasser.newshapes.shapes.Shape;
|
||||||
import ovh.gasser.newshapes.ui.ShapesView;
|
import ovh.gasser.newshapes.ui.ShapesView;
|
||||||
import ovh.gasser.newshapes.ui.menu.MenuAddListener;
|
import ovh.gasser.newshapes.ui.listeners.MenuAddListener;
|
||||||
import ovh.gasser.newshapes.ui.menu.MenuEditListener;
|
import ovh.gasser.newshapes.ui.listeners.MenuEditListener;
|
||||||
|
import ovh.gasser.newshapes.ui.listeners.SelectionListener;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public class App {
|
public class App {
|
||||||
private final static Logger logger = LoggerFactory.getLogger(App.class);
|
private final static Logger logger = LoggerFactory.getLogger(App.class);
|
||||||
public static final Dimension WIN_SIZE = new Dimension(800, 600);
|
public static final Dimension WIN_SIZE = new Dimension(800, 600);
|
||||||
private SCollection model;
|
private SCollection model;
|
||||||
|
private JCheckBoxMenuItem editFill;
|
||||||
|
private JCheckBoxMenuItem editBorder;
|
||||||
|
|
||||||
private App() throws HeadlessException {
|
private App() throws HeadlessException {
|
||||||
final JFrame frame = new JFrame("Reactive shapes");
|
final JFrame frame = new JFrame("Reactive shapes");
|
||||||
@@ -35,6 +41,13 @@ public class App {
|
|||||||
frame.setVisible(true);
|
frame.setVisible(true);
|
||||||
|
|
||||||
this.buildMenuBar(frame, view);
|
this.buildMenuBar(frame, view);
|
||||||
|
|
||||||
|
view.addSelectionChangeListener(new SelectionListener() {
|
||||||
|
@Override
|
||||||
|
public void onSelectionChanged(Iterable<Shape> selectedShapes) {
|
||||||
|
updateMenuState(selectedShapes);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void buildModel() {
|
private void buildModel() {
|
||||||
@@ -94,8 +107,8 @@ public class App {
|
|||||||
JMenuItem editColor = new JMenuItem("Change color");
|
JMenuItem editColor = new JMenuItem("Change color");
|
||||||
JMenuItem editBorderColor = new JMenuItem("Change border color");
|
JMenuItem editBorderColor = new JMenuItem("Change border color");
|
||||||
JMenuItem deleteItem = new JMenuItem("Delete");
|
JMenuItem deleteItem = new JMenuItem("Delete");
|
||||||
JCheckBoxMenuItem editFill = new JCheckBoxMenuItem("Fill Shape");
|
editFill = new JCheckBoxMenuItem("Fill Shape");
|
||||||
JCheckBoxMenuItem editBorder = new JCheckBoxMenuItem("Draw border");
|
editBorder = new JCheckBoxMenuItem("Draw border");
|
||||||
editColor.addActionListener(editListener);
|
editColor.addActionListener(editListener);
|
||||||
editBorderColor.addActionListener(editListener);
|
editBorderColor.addActionListener(editListener);
|
||||||
deleteItem.addActionListener(editListener);
|
deleteItem.addActionListener(editListener);
|
||||||
@@ -111,6 +124,34 @@ public class App {
|
|||||||
return menuEdit;
|
return menuEdit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateMenuState(Iterable<Shape> selectedShapes) {
|
||||||
|
boolean hasAttributes = false;
|
||||||
|
boolean allFilled = true;
|
||||||
|
boolean allStroked = true;
|
||||||
|
|
||||||
|
for (Shape s : selectedShapes) {
|
||||||
|
ColorAttributes attrs = (ColorAttributes) s.getAttributes(ColorAttributes.ID);
|
||||||
|
if (attrs != null) {
|
||||||
|
hasAttributes = true;
|
||||||
|
allFilled = allFilled && attrs.filled;
|
||||||
|
allStroked = allStroked && attrs.stroked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateMenuItem(editFill, hasAttributes, allFilled);
|
||||||
|
updateMenuItem(editBorder, hasAttributes, allStroked);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateMenuItem(JCheckBoxMenuItem menuItem, boolean hasAttributes, boolean allSelected) {
|
||||||
|
if (!hasAttributes) {
|
||||||
|
menuItem.setSelected(false);
|
||||||
|
menuItem.setEnabled(false);
|
||||||
|
} else {
|
||||||
|
menuItem.setSelected(allSelected);
|
||||||
|
menuItem.setEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SwingUtilities.invokeLater(App::new);
|
SwingUtilities.invokeLater(App::new);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,22 @@
|
|||||||
package ovh.gasser.newshapes;
|
package ovh.gasser.newshapes;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import ovh.gasser.newshapes.attributes.SelectionAttributes;
|
import ovh.gasser.newshapes.attributes.SelectionAttributes;
|
||||||
import ovh.gasser.newshapes.shapes.Shape;
|
import ovh.gasser.newshapes.shapes.Shape;
|
||||||
|
import ovh.gasser.newshapes.ui.listeners.SelectionListener;
|
||||||
import ovh.gasser.newshapes.util.Streamable;
|
import ovh.gasser.newshapes.util.Streamable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class Selection implements Streamable<Shape> {
|
public class Selection implements Streamable<Shape> {
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(Selection.class);
|
||||||
|
|
||||||
private final List<Shape> selectedShapes = new ArrayList<>();
|
private final List<Shape> selectedShapes = new ArrayList<>();
|
||||||
|
private final List<SelectionListener> listeners = new ArrayList<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterator<Shape> iterator() {
|
public Iterator<Shape> iterator() {
|
||||||
@@ -20,12 +27,26 @@ public class Selection implements Streamable<Shape> {
|
|||||||
for (Shape shape : selectedShapes) {
|
for (Shape shape : selectedShapes) {
|
||||||
shape.addAttributes(new SelectionAttributes(false));
|
shape.addAttributes(new SelectionAttributes(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedShapes.clear();
|
selectedShapes.clear();
|
||||||
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void add(Shape s) {
|
public void add(Shape s) {
|
||||||
|
logger.info("Selection.add");
|
||||||
selectedShapes.add(s);
|
selectedShapes.add(s);
|
||||||
s.addAttributes(new SelectionAttributes(true));
|
s.addAttributes(new SelectionAttributes(true));
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Shape> getSelectedShapes() {
|
||||||
|
return List.copyOf(selectedShapes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addListener(SelectionListener listener) {
|
||||||
|
this.listeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyListeners(){
|
||||||
|
listeners.forEach(l -> l.onSelectionChanged(Collections.unmodifiableCollection(selectedShapes)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,14 +4,15 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import ovh.gasser.newshapes.App;
|
import ovh.gasser.newshapes.App;
|
||||||
import ovh.gasser.newshapes.ShapeVisitor;
|
import ovh.gasser.newshapes.ShapeVisitor;
|
||||||
|
import ovh.gasser.newshapes.attributes.Attributes;
|
||||||
|
import ovh.gasser.newshapes.attributes.ColorAttributes;
|
||||||
import ovh.gasser.newshapes.attributes.SelectionAttributes;
|
import ovh.gasser.newshapes.attributes.SelectionAttributes;
|
||||||
import ovh.gasser.newshapes.util.Streamable;
|
import ovh.gasser.newshapes.util.Streamable;
|
||||||
|
|
||||||
|
import javax.swing.text.html.Option;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Spliterator;
|
|
||||||
|
|
||||||
public class SCollection extends AbstractShape implements Streamable<Shape> {
|
public class SCollection extends AbstractShape implements Streamable<Shape> {
|
||||||
private final static Logger logger = LoggerFactory.getLogger(SCollection.class);
|
private final static Logger logger = LoggerFactory.getLogger(SCollection.class);
|
||||||
@@ -38,7 +39,7 @@ public class SCollection extends AbstractShape implements Streamable<Shape> {
|
|||||||
for (Shape s : children) bounds = bounds.union(s.getBounds());
|
for (Shape s : children) bounds = bounds.union(s.getBounds());
|
||||||
return bounds;
|
return bounds;
|
||||||
} catch (IndexOutOfBoundsException e){
|
} catch (IndexOutOfBoundsException e){
|
||||||
logger.error("getBounds(): {}");
|
logger.error("getBounds(): {e}", e);
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -76,6 +77,27 @@ public class SCollection extends AbstractShape implements Streamable<Shape> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Attributes getAttributes(String key) {
|
||||||
|
if (key.equals(ColorAttributes.ID)) {
|
||||||
|
// If the shape is a collection, it does not support color attributes directly
|
||||||
|
// For now, use the attributes from the first child shape
|
||||||
|
Optional<Shape> first = children.stream().findFirst();
|
||||||
|
return first.map(shape -> shape.getAttributes(key)).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.getAttributes(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addAttributes(Attributes attrs) {
|
||||||
|
// Propagate color attributes to children
|
||||||
|
if (attrs.getID().equals(ColorAttributes.ID)) {
|
||||||
|
children.forEach(shape -> shape.addAttributes(attrs));
|
||||||
|
}
|
||||||
|
super.addAttributes(attrs);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder sb = new StringBuilder("SCollection{");
|
StringBuilder sb = new StringBuilder("SCollection{");
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import ovh.gasser.newshapes.Selection;
|
|||||||
import ovh.gasser.newshapes.attributes.ColorAttributes;
|
import ovh.gasser.newshapes.attributes.ColorAttributes;
|
||||||
import ovh.gasser.newshapes.shapes.SCollection;
|
import ovh.gasser.newshapes.shapes.SCollection;
|
||||||
import ovh.gasser.newshapes.shapes.Shape;
|
import ovh.gasser.newshapes.shapes.Shape;
|
||||||
|
import ovh.gasser.newshapes.ui.listeners.SelectionListener;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.KeyAdapter;
|
import java.awt.event.KeyAdapter;
|
||||||
@@ -21,6 +22,7 @@ public class Controller {
|
|||||||
private final ShapesView view;
|
private final ShapesView view;
|
||||||
private final SCollection model;
|
private final SCollection model;
|
||||||
private final Selection selection;
|
private final Selection selection;
|
||||||
|
|
||||||
private Point lastMousePos;
|
private Point lastMousePos;
|
||||||
|
|
||||||
Controller(ShapesView view, SCollection model) {
|
Controller(ShapesView view, SCollection model) {
|
||||||
@@ -139,10 +141,13 @@ public class Controller {
|
|||||||
selection.clear();
|
selection.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addSelectionChangeListener(SelectionListener listener) {
|
||||||
|
selection.addListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
private Optional<Shape> getTarget(MouseEvent evt, SCollection sc) {
|
private Optional<Shape> getTarget(MouseEvent evt, SCollection sc) {
|
||||||
return sc.stream()
|
return sc.stream()
|
||||||
.filter(s -> s.getBounds().contains(evt.getPoint()))
|
.filter(s -> s.getBounds().contains(evt.getPoint()))
|
||||||
.findFirst();
|
.findFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,14 @@
|
|||||||
package ovh.gasser.newshapes.ui;
|
package ovh.gasser.newshapes.ui;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import ovh.gasser.newshapes.ShapeVisitor;
|
import ovh.gasser.newshapes.ShapeVisitor;
|
||||||
import ovh.gasser.newshapes.shapes.SCollection;
|
import ovh.gasser.newshapes.shapes.SCollection;
|
||||||
import ovh.gasser.newshapes.shapes.Shape;
|
import ovh.gasser.newshapes.shapes.Shape;
|
||||||
|
import ovh.gasser.newshapes.ui.listeners.SelectionListener;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
|
||||||
public class ShapesView extends JPanel {
|
public class ShapesView extends JPanel {
|
||||||
final Logger logger = LoggerFactory.getLogger(ShapesView.class);
|
|
||||||
|
|
||||||
private final Shape model;
|
private final Shape model;
|
||||||
private final Controller controller;
|
private final Controller controller;
|
||||||
private ShapeVisitor draftman;
|
private ShapeVisitor draftman;
|
||||||
@@ -31,4 +28,8 @@ public class ShapesView extends JPanel {
|
|||||||
public Controller getController() {
|
public Controller getController() {
|
||||||
return controller;
|
return controller;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addSelectionChangeListener(SelectionListener listener) {
|
||||||
|
controller.addSelectionChangeListener(listener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package ovh.gasser.newshapes.ui.menu;
|
package ovh.gasser.newshapes.ui.listeners;
|
||||||
|
|
||||||
import ovh.gasser.newshapes.attributes.ColorAttributes;
|
import ovh.gasser.newshapes.attributes.ColorAttributes;
|
||||||
import ovh.gasser.newshapes.attributes.SelectionAttributes;
|
import ovh.gasser.newshapes.attributes.SelectionAttributes;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package ovh.gasser.newshapes.ui.menu;
|
package ovh.gasser.newshapes.ui.listeners;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package ovh.gasser.newshapes.ui.listeners;
|
||||||
|
|
||||||
|
import ovh.gasser.newshapes.shapes.Shape;
|
||||||
|
|
||||||
|
public interface SelectionListener {
|
||||||
|
void onSelectionChanged(Iterable<Shape> selectedShapes);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user