Compare commits

..

2 Commits

Author SHA1 Message Date
bb9f4fbf39 Rename rxshapes to newshapes 2019-03-19 21:17:53 +01:00
e8840cfd8b Base RXJava POC 2019-03-19 20:41:21 +01:00
10 changed files with 97 additions and 139 deletions

View File

@ -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>

View File

@ -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)
)
);
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -1,65 +1,48 @@
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;
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 -> {
if (selection != null) resetSelection();
selection = new Selection(s, true);
logger.debug("Selecting {}", selection.shape);
},
s -> selection = new Selection(s, true),
() -> {
if (selection != null) resetSelection();
}
);
view.repaint();
}
private void resetSelection() {
logger.debug("Un-selecting {}", selection.shape);
if (selection != null) {
selection.unselect();
selection = null;
}
}
);
break;
case MouseEvent.MOUSE_DRAGGED:
handleMouseMoved(evt);
break;
}
view.repaint();
}, err -> logger.error("{}", err)
);
}
private Optional<Shape> getTarget(MouseEvent evt, SCollection sc) {
return sc.stream()
@ -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();
}
}

View File

@ -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)){

View File

@ -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);

View 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));
});
}
}

View 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>