Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
bb9f4fbf39 | |||
e8840cfd8b |
7
pom.xml
7
pom.xml
@ -15,6 +15,13 @@
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<dependencies>
|
||||
<!-- https://mvnrepository.com/artifact/io.reactivex.rxjava2/rxjava -->
|
||||
<dependency>
|
||||
<groupId>io.reactivex.rxjava2</groupId>
|
||||
<artifactId>rxjava</artifactId>
|
||||
<version>2.2.7</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
|
@ -1,6 +1,5 @@
|
||||
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;
|
||||
@ -32,8 +31,7 @@ public class App {
|
||||
SRectangle.create(70, 10, 40, 60),
|
||||
SCollection.of(
|
||||
SRectangle.create(100, 200, 40, 60, Color.MAGENTA),
|
||||
SRectangle.create(150, 200, 40, 60, Color.MAGENTA),
|
||||
SCircle.create(200, 200, 60)
|
||||
SRectangle.create(150, 200, 40, 60, Color.MAGENTA)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package ovh.gasser.newshapes;
|
||||
|
||||
import ovh.gasser.newshapes.shapes.SCircle;
|
||||
import ovh.gasser.newshapes.shapes.SCollection;
|
||||
import ovh.gasser.newshapes.shapes.SRectangle;
|
||||
|
||||
@ -8,6 +7,4 @@ public interface ShapeVisitor {
|
||||
void visitRectangle(SRectangle sRectangle);
|
||||
|
||||
void visitCollection(SCollection collection);
|
||||
|
||||
void visitCircle(SCircle sCircle);
|
||||
}
|
||||
|
@ -1,31 +0,0 @@
|
||||
package ovh.gasser.newshapes.shapes;
|
||||
|
||||
import ovh.gasser.newshapes.ShapeVisitor;
|
||||
import ovh.gasser.newshapes.attributes.SelectionAttributes;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
public class SCircle extends AbstractShape {
|
||||
|
||||
private int radius;
|
||||
|
||||
private SCircle(int x, int y, int radius) {
|
||||
super(new Rectangle(x, y, radius, radius));
|
||||
this.radius = radius;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(ShapeVisitor visitor) {
|
||||
visitor.visitCircle(this);
|
||||
}
|
||||
|
||||
public int getRadius() {
|
||||
return radius;
|
||||
}
|
||||
|
||||
public static SCircle create(int x, int y, int radius) {
|
||||
var circle = new SCircle(x, y, radius);
|
||||
circle.addAttributes(new SelectionAttributes());
|
||||
return circle;
|
||||
}
|
||||
}
|
@ -1,10 +1,8 @@
|
||||
package ovh.gasser.newshapes.shapes;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import ovh.gasser.newshapes.App;
|
||||
import ovh.gasser.newshapes.ShapeVisitor;
|
||||
import ovh.gasser.newshapes.attributes.SelectionAttributes;
|
||||
import ovh.gasser.newshapes.ShapeVisitor;
|
||||
import ovh.gasser.newshapes.util.Streamable;
|
||||
|
||||
import java.awt.*;
|
||||
@ -13,7 +11,6 @@ import java.util.List;
|
||||
import java.util.Spliterator;
|
||||
|
||||
public class SCollection extends AbstractShape implements Streamable<Shape> {
|
||||
private final static Logger logger = LoggerFactory.getLogger(SCollection.class);
|
||||
private final List<Shape> children;
|
||||
|
||||
private SCollection(Shape... shapes) {
|
||||
@ -28,17 +25,12 @@ public class SCollection extends AbstractShape implements Streamable<Shape> {
|
||||
@Override
|
||||
public Rectangle getBounds() {
|
||||
try {
|
||||
if (children.isEmpty()) {
|
||||
// If the SCollection is empty, set the bounds to fill the window
|
||||
return new Rectangle(App.WIN_SIZE);
|
||||
}
|
||||
|
||||
Rectangle bounds = children.get(0).getBounds();
|
||||
for (Shape s : children) bounds = bounds.union(s.getBounds());
|
||||
return bounds;
|
||||
} catch (IndexOutOfBoundsException e){
|
||||
logger.error("getBounds(): {}");
|
||||
throw new RuntimeException(e);
|
||||
// If the SCollection is empty, set the bounds to fill the window
|
||||
return new Rectangle(App.WIN_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,64 +1,47 @@
|
||||
package ovh.gasser.newshapes.ui;
|
||||
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import ovh.gasser.newshapes.Selection;
|
||||
import ovh.gasser.newshapes.shapes.SCollection;
|
||||
import ovh.gasser.newshapes.util.EventSource;
|
||||
import ovh.gasser.newshapes.shapes.Shape;
|
||||
import ovh.gasser.newshapes.shapes.SCollection;
|
||||
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.Optional;
|
||||
|
||||
public class Controller {
|
||||
private final static Logger logger = LoggerFactory.getLogger(ShapesView.class);
|
||||
private final ShapesView view;
|
||||
private final Shape model;
|
||||
private final Logger logger = LoggerFactory.getLogger(ShapesView.class);
|
||||
private final Disposable mouseSub;
|
||||
private Selection selection;
|
||||
|
||||
Controller(ShapesView view, Shape model) {
|
||||
this.view = view;
|
||||
this.model = model;
|
||||
var adapter = new MouseAdapter() {
|
||||
@Override
|
||||
public void mousePressed(MouseEvent evt) {
|
||||
handleMousePressed(evt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(MouseEvent evt) {
|
||||
handleMouseDragged(evt);
|
||||
}
|
||||
};
|
||||
this.view.addMouseMotionListener(adapter);
|
||||
this.view.addMouseListener(adapter);
|
||||
}
|
||||
|
||||
private void handleMouseDragged(MouseEvent evt) {
|
||||
if (selection != null) selection.shape.setLoc(evt.getPoint());
|
||||
view.repaint();
|
||||
}
|
||||
|
||||
private void handleMousePressed(MouseEvent evt) {
|
||||
var sc = (SCollection) this.model;
|
||||
getTarget(evt, sc)
|
||||
.ifPresentOrElse(
|
||||
s -> {
|
||||
if (selection != null) resetSelection();
|
||||
selection = new Selection(s, true);
|
||||
logger.debug("Selecting {}", selection.shape);
|
||||
},
|
||||
() -> {
|
||||
if (selection != null) resetSelection();
|
||||
}
|
||||
mouseSub = EventSource
|
||||
.fromMouseEventsOf(view)
|
||||
.subscribe(evt -> {
|
||||
assert model instanceof SCollection;
|
||||
SCollection sc = (SCollection) model;
|
||||
switch (evt.getID()) {
|
||||
case MouseEvent.MOUSE_PRESSED:
|
||||
getTarget(evt, sc)
|
||||
.ifPresentOrElse(
|
||||
s -> selection = new Selection(s, true),
|
||||
() -> {
|
||||
if (selection != null) {
|
||||
selection.unselect();
|
||||
selection = null;
|
||||
}
|
||||
}
|
||||
);
|
||||
break;
|
||||
case MouseEvent.MOUSE_DRAGGED:
|
||||
handleMouseMoved(evt);
|
||||
break;
|
||||
}
|
||||
view.repaint();
|
||||
}, err -> logger.error("{}", err)
|
||||
);
|
||||
view.repaint();
|
||||
}
|
||||
|
||||
private void resetSelection() {
|
||||
logger.debug("Un-selecting {}", selection.shape);
|
||||
selection.unselect();
|
||||
selection = null;
|
||||
}
|
||||
|
||||
private Optional<Shape> getTarget(MouseEvent evt, SCollection sc) {
|
||||
@ -67,4 +50,12 @@ public class Controller {
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
private void handleMouseMoved(MouseEvent evt) {
|
||||
if (selection != null) selection.shape.setLoc(evt.getPoint());
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
logger.info("Cleaning subscriptions...");
|
||||
mouseSub.dispose();
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,11 @@
|
||||
package ovh.gasser.newshapes.ui;
|
||||
|
||||
import ovh.gasser.newshapes.ShapeVisitor;
|
||||
import ovh.gasser.newshapes.shapes.Shape;
|
||||
import ovh.gasser.newshapes.attributes.ColorAttributes;
|
||||
import ovh.gasser.newshapes.attributes.SelectionAttributes;
|
||||
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 java.awt.*;
|
||||
|
||||
@ -47,23 +46,6 @@ public class ShapeDraftman implements ShapeVisitor {
|
||||
drawHandlerIfSelected(collection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitCircle(SCircle circle) {
|
||||
ColorAttributes colAttrs = (ColorAttributes) circle.getAttributes(ColorAttributes.ID);
|
||||
final Rectangle bounds = circle.getBounds();
|
||||
if (colAttrs == null) {
|
||||
colAttrs = DEFAULT_COLOR_ATTRIBUTES;
|
||||
}
|
||||
if (colAttrs.filled) {
|
||||
this.g2d.setColor(colAttrs.filledColor);
|
||||
this.g2d.fillOval(bounds.x, bounds.y, circle.getRadius(), circle.getRadius());
|
||||
}
|
||||
if (colAttrs.stroked) this.g2d.setColor(colAttrs.strokedColor);
|
||||
this.g2d.drawOval(bounds.x, bounds.y, circle.getRadius(), circle.getRadius());
|
||||
|
||||
drawHandlerIfSelected(circle);
|
||||
}
|
||||
|
||||
private void drawHandlerIfSelected(Shape s) {
|
||||
SelectionAttributes selAttrs = (SelectionAttributes) s.getAttributes(SelectionAttributes.ID);
|
||||
if ((selAttrs != null) && (selAttrs.selected)){
|
||||
|
@ -2,10 +2,8 @@ package ovh.gasser.newshapes.ui.html;
|
||||
|
||||
import ovh.gasser.newshapes.ShapeVisitor;
|
||||
import ovh.gasser.newshapes.attributes.ColorAttributes;
|
||||
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 java.awt.*;
|
||||
import java.io.PrintWriter;
|
||||
@ -31,11 +29,6 @@ public class HTMLDraftman implements ShapeVisitor {
|
||||
this.cssOutput = cssOutput;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitCollection(SCollection collection) {
|
||||
collection.stream().forEach(shape -> shape.accept(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitRectangle(SRectangle rect) {
|
||||
htmlOutput.println("<div id=\"rec" + rect.hashCode() + "\"></div>");
|
||||
@ -48,25 +41,8 @@ public class HTMLDraftman implements ShapeVisitor {
|
||||
cssOutput.println(this.attributesToCss(rect) + " }");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitCircle(SCircle circle) {
|
||||
htmlOutput.println("<div class=\"circle"+circle.hashCode()+"\"></div>");
|
||||
cssOutput.println(".circle" + circle.hashCode() + "{ ");
|
||||
cssOutput.println("position: absolute;");
|
||||
cssOutput.println("top:" + circle.getBounds().y + "px;");
|
||||
cssOutput.println("left:" + circle.getBounds().x + "px;");
|
||||
cssOutput.println("width:" + circle.getRadius() + "px;");
|
||||
cssOutput.println("height:" + circle.getRadius() + "px;");
|
||||
cssOutput.println("border-radius:" + circle.getRadius() / 2 + "px;");
|
||||
cssOutput.println("-webkit-border-radius:" + circle.getRadius() / 2 + "px;");
|
||||
cssOutput.println("-o-border-radius:" + circle.getRadius() / 2 + "px;");
|
||||
cssOutput.println("-moz-border-radius:" + circle.getRadius() / 2 + "px;");
|
||||
cssOutput.println("position: absolute;");
|
||||
cssOutput.println(this.attributesToCss(circle) + " }");
|
||||
}
|
||||
|
||||
private String attributesToCss(Shape shape) {
|
||||
ColorAttributes attrs = (ColorAttributes) shape.getAttributes(ColorAttributes.ID);
|
||||
private String attributesToCss(SRectangle rect) {
|
||||
ColorAttributes attrs = (ColorAttributes) rect.getAttributes(ColorAttributes.ID);
|
||||
String strokedColor = "#ffffff";
|
||||
String filledColor = "#ffffff";
|
||||
|
||||
@ -96,6 +72,11 @@ public class HTMLDraftman implements ShapeVisitor {
|
||||
return String.format("#%02x%02x%02x", r, g, b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitCollection(SCollection collection) {
|
||||
collection.stream().forEach(shape -> shape.accept(this));
|
||||
}
|
||||
|
||||
public void generateHTML(SCollection model) {
|
||||
htmlOutput.println(HEADER_TEMPLATE);
|
||||
visitCollection(model);
|
||||
|
28
src/main/java/ovh/gasser/newshapes/util/EventSource.java
Normal file
28
src/main/java/ovh/gasser/newshapes/util/EventSource.java
Normal file
@ -0,0 +1,28 @@
|
||||
package ovh.gasser.newshapes.util;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
public class EventSource {
|
||||
public static Observable<MouseEvent> fromMouseEventsOf(final Component component) {
|
||||
return Observable.create(emitter -> {
|
||||
final MouseAdapter adapter = new MouseAdapter() {
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
emitter.onNext(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(MouseEvent e) {
|
||||
emitter.onNext(e);
|
||||
}
|
||||
};
|
||||
component.addMouseMotionListener(adapter);
|
||||
component.addMouseListener(adapter);
|
||||
emitter.setCancellable(() -> component.removeMouseListener(adapter));
|
||||
});
|
||||
}
|
||||
}
|
13
src/main/resources/logback.xml
Normal file
13
src/main/resources/logback.xml
Normal file
@ -0,0 +1,13 @@
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<withJansi>true</withJansi>
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} %highlight(%-5level) [%10thread] %cyan(%-40logger{36}) - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="trace">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
</configuration>
|
||||
|
Loading…
Reference in New Issue
Block a user