feat(network): création de canvas à distance

This commit is contained in:
Emi Boucly 2025-03-27 09:50:53 +01:00
parent de645a8b1e
commit 574dc13c06
7 changed files with 154 additions and 51 deletions

View file

@ -1,6 +1,7 @@
package fr.emiko.graphicalapp; package fr.emiko.graphicalapp;
import fr.emiko.graphicsElement.Line; import fr.emiko.graphicsElement.Line;
import fr.emiko.graphicsElement.layerListViewCell;
import fr.emiko.net.DrawClient; import fr.emiko.net.DrawClient;
import fr.emiko.net.DrawServer; import fr.emiko.net.DrawServer;
import fr.emiko.net.Event; import fr.emiko.net.Event;
@ -46,7 +47,6 @@ public class HelloController implements Initializable {
public MenuItem loadButton; public MenuItem loadButton;
public MenuItem newCanvasButton; public MenuItem newCanvasButton;
public Slider brushSizeSlider; public Slider brushSizeSlider;
public Slider zoomSlider;
public ScrollPane scrollPane; public ScrollPane scrollPane;
public Label brushSizeLabel; public Label brushSizeLabel;
public Pane pane; public Pane pane;
@ -56,7 +56,7 @@ public class HelloController implements Initializable {
public SplitPane mainPane; public SplitPane mainPane;
public MenuItem stopHostButton; public MenuItem stopHostButton;
public ColorPicker colorPicker; public ColorPicker colorPicker;
public ListView layerListView; public ListView<Canvas> layerListView;
public Button addLayerButton; public Button addLayerButton;
public Button removeLayerButton; public Button removeLayerButton;
private double posX = 0; private double posX = 0;
@ -71,7 +71,6 @@ public class HelloController implements Initializable {
private DrawClient client; private DrawClient client;
private ToggleButton hostButtonToggle = new ToggleButton(); private ToggleButton hostButtonToggle = new ToggleButton();
private DrawServer server; private DrawServer server;
private Canvas currentLayer;
private ObservableList<Canvas> layerObservableList = FXCollections.observableArrayList(); private ObservableList<Canvas> layerObservableList = FXCollections.observableArrayList();
@ -83,7 +82,7 @@ public class HelloController implements Initializable {
scrollPane.setOnScroll(this::onScrollZoom); scrollPane.setOnScroll(this::onScrollZoom);
scrollPane.setOnKeyPressed(this::onActionKeyPressed); scrollPane.setOnKeyPressed(this::onActionKeyPressed);
brushSizeLabel.textProperty().bind(brushSizeSlider.valueProperty().asString()); brushSizeLabel.textProperty().bind(brushSizeSlider.valueProperty().asString());
setupCanvas(); setupCanvas(drawingCanvas);
scrollPane.prefViewportHeightProperty().bind(pane.layoutYProperty()); scrollPane.prefViewportHeightProperty().bind(pane.layoutYProperty());
scrollPane.prefViewportWidthProperty().bind(pane.layoutXProperty()); scrollPane.prefViewportWidthProperty().bind(pane.layoutXProperty());
@ -92,12 +91,42 @@ public class HelloController implements Initializable {
joinButton.setOnAction(this::onActionJoin); joinButton.setOnAction(this::onActionJoin);
disconnectButton.setOnAction(this::onActionDisconnect); disconnectButton.setOnAction(this::onActionDisconnect);
newCanvasButton.disableProperty().bind(hostButtonToggle.selectedProperty().not());
stopHostButton.disableProperty().bind(hostButtonToggle.selectedProperty().not()); stopHostButton.disableProperty().bind(hostButtonToggle.selectedProperty().not());
disconnectButton.disableProperty().bind(hostButtonToggle.selectedProperty().not()); disconnectButton.disableProperty().bind(hostButtonToggle.selectedProperty().not());
hostButtonToggle.setSelected(false); hostButtonToggle.setSelected(false);
mainPane.disableProperty().bind(hostButtonToggle.selectedProperty().not()); mainPane.disableProperty().bind(hostButtonToggle.selectedProperty().not());
layerListView.setCellFactory(layerListView -> new layerListViewCell());
layerListView.setItems(layerObservableList); layerListView.setItems(layerObservableList);
layerListView.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
//addLayerButton.setOnAction(this::onActionAddLayer);
//removeLayerButton.setOnAction(this::onActionRemoveLayer);
//layerListView.setOnMouseClicked(this::onActionSelectCanvas);
}
private void onActionSelectCanvas(MouseEvent mouseEvent) {
layerListView.getSelectionModel().getSelectedItem().requestFocus();
layerListView.getSelectionModel().getSelectedItem().toFront();
}
private void onActionRemoveLayer(ActionEvent actionEvent) {
pane.getChildren().remove(layerListView.getSelectionModel().getSelectedItem());
layerObservableList.remove(layerListView.getSelectionModel().getSelectedItem());
layerListView.refresh();
layerListView.getSelectionModel().select(layerObservableList.getFirst());
}
private void onActionAddLayer(ActionEvent actionEvent) {
Canvas newLayer = new Canvas(
layerListView.getSelectionModel().getSelectedItem().getWidth(),
layerListView.getSelectionModel().getSelectedItem().getHeight()
);
pane.getChildren().add(newLayer);
layerObservableList.addFirst(newLayer);
layerListView.getSelectionModel().select(newLayer);
setupCanvas(newLayer);
layerListView.refresh();
} }
private void onActionStopHost(ActionEvent actionEvent) { private void onActionStopHost(ActionEvent actionEvent) {
@ -109,10 +138,12 @@ public class HelloController implements Initializable {
showErrorDialog(e, "Could not close server instance"); showErrorDialog(e, "Could not close server instance");
} }
} }
hostButtonToggle.setSelected(false);
} }
private void onActionDisconnect(ActionEvent actionEvent) { private void onActionDisconnect(ActionEvent actionEvent) {
client.close(); client.close();
hostButtonToggle.setSelected(false);
} }
@ -129,6 +160,7 @@ public class HelloController implements Initializable {
String host = matcher.group(1); String host = matcher.group(1);
String port = matcher.group(2); String port = matcher.group(2);
connectClient(host, port == null ? 8090 : Integer.parseInt(port)); connectClient(host, port == null ? 8090 : Integer.parseInt(port));
client.sendEvent(new Event(Event.LINELST, new JSONObject()));
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
showErrorDialog(e, "Invalid distant address"); showErrorDialog(e, "Invalid distant address");
} catch (IOException e) { } catch (IOException e) {
@ -194,15 +226,20 @@ public class HelloController implements Initializable {
alert.showAndWait(); alert.showAndWait();
} }
private void setupCanvas() { private void setupCanvas(Canvas canvas) {
drawingCanvas.requestFocus(); canvas.requestFocus();
drawingCanvas.getGraphicsContext2D().setFill(Color.WHITE); canvas.getGraphicsContext2D().setFill(Color.WHITE);
drawingCanvas.getGraphicsContext2D().fillRect(0, 0, drawingCanvas.getWidth(), drawingCanvas.getHeight()); canvas.getGraphicsContext2D().fillRect(0, 0, drawingCanvas.getWidth(), drawingCanvas.getHeight());
brushSizeSlider.setValue(1); brushSizeSlider.setValue(1);
drawingCanvas.setTranslateX(scrollPane.getWidth()/2); // canvas.setTranslateX(scrollPane.getWidth()/2);
drawingCanvas.setTranslateY(scrollPane.getHeight()/2); // canvas.setTranslateY(scrollPane.getHeight()/2);
drawingCanvas.setOnMouseDragged(this::printLine); colorPicker.setValue(Color.BLACK);
drawingCanvas.setOnMouseClicked(this::resetPos); canvas.setOnMouseDragged(this::printLine);
canvas.setOnMouseClicked(this::resetPos);
layerListView.getSelectionModel().select(drawingCanvas);
layerObservableList.add(drawingCanvas);
layerListView.refresh();
scrollPane.addEventFilter(ScrollEvent.ANY, new EventHandler<ScrollEvent>() { scrollPane.addEventFilter(ScrollEvent.ANY, new EventHandler<ScrollEvent>() {
@Override @Override
public void handle(ScrollEvent event) { public void handle(ScrollEvent event) {
@ -221,10 +258,14 @@ public class HelloController implements Initializable {
if (keyEvent.isControlDown() && keyEvent.getCode().equals(KeyCode.Z)) { if (keyEvent.isControlDown() && keyEvent.getCode().equals(KeyCode.Z)) {
System.out.println("CTRL Z"); System.out.println("CTRL Z");
System.out.println(lines); System.out.println(lines);
System.out.println(lines);
lines.remove(lines.lastElement()); lines.remove(lines.lastElement());
GraphicsContext gc = drawingCanvas.getGraphicsContext2D(); Canvas currentLayer = layerListView.getSelectionModel().getSelectedItem();
GraphicsContext gc = currentLayer.getGraphicsContext2D();
gc.setFill(Color.WHITE); gc.setFill(Color.WHITE);
gc.fillRect(0, 0, drawingCanvas.getWidth(), drawingCanvas.getHeight()); gc.fillRect(0, 0, currentLayer.getWidth(), currentLayer.getHeight());
gc.clearRect(0, 0, currentLayer.getWidth(), currentLayer.getHeight());
gc.fill();
for (Vector<Stroke> strokeVector : lines) { for (Vector<Stroke> strokeVector : lines) {
for (Stroke stroke: strokeVector) { for (Stroke stroke: strokeVector) {
stroke.draw(gc, stroke.getColor()); stroke.draw(gc, stroke.getColor());
@ -277,6 +318,7 @@ public class HelloController implements Initializable {
drawingCanvas.getGraphicsContext2D().fill(); drawingCanvas.getGraphicsContext2D().fill();
pane.setScaleX(1); pane.setScaleX(1);
pane.setScaleY(1); pane.setScaleY(1);
client.sendEvent(new Event(Event.ADDCANVAS, new JSONObject().put("width", drawingCanvas.getWidth()).put("height", drawingCanvas.getHeight())));
System.out.println("New canvas created"); System.out.println("New canvas created");
} }
} catch (IOException ignored) { } catch (IOException ignored) {
@ -337,8 +379,9 @@ public class HelloController implements Initializable {
} }
private void printLine(MouseEvent mouseEvent) { private void printLine(MouseEvent mouseEvent) {
Canvas currentLayer = layerListView.getSelectionModel().getSelectedItem();
if (mouseEvent.isPrimaryButtonDown()) { if (mouseEvent.isPrimaryButtonDown()) {
GraphicsContext gc = drawingCanvas.getGraphicsContext2D(); GraphicsContext gc = currentLayer.getGraphicsContext2D();
if (posX == 0 || posY == 0) { if (posX == 0 || posY == 0) {
posX = mouseEvent.getX(); posX = mouseEvent.getX();
@ -354,7 +397,19 @@ public class HelloController implements Initializable {
} else if (mouseEvent.isSecondaryButtonDown()) { } else if (mouseEvent.isSecondaryButtonDown()) {
GraphicsContext gc = currentLayer.getGraphicsContext2D();
if (posX == 0 || posY == 0) {
posX = mouseEvent.getX();
posY = mouseEvent.getY();
}
Stroke stroke = new Stroke(posX, posY, mouseEvent.getX(), mouseEvent.getY(), brushSizeSlider.getValue(), colorPicker.getValue());
strokes.add(stroke);
stroke.draw(gc, Color.WHITE);
posX = mouseEvent.getX();
posY = mouseEvent.getY();
} }
} }
@ -368,10 +423,25 @@ public class HelloController implements Initializable {
case Event.DELLINE -> { case Event.DELLINE -> {
doDeleteLine(event.getContent()); doDeleteLine(event.getContent());
} }
case Event.CNVS -> {
doAddCanvas(event.getContent());
}
default -> {} default -> {}
} }
} }
private void doAddCanvas(JSONObject content) {
drawingCanvas.setWidth(content.getDouble("width"));
drawingCanvas.setHeight(content.getDouble("height"));
drawingCanvas.getGraphicsContext2D().setFill(Color.WHITE);
drawingCanvas.getGraphicsContext2D().fillRect(0, 0, drawingCanvas.getWidth(), drawingCanvas.getHeight());
drawingCanvas.getGraphicsContext2D().fill();
pane.setScaleX(1);
pane.setScaleY(1);
setupCanvas(drawingCanvas);
}
private void doDeleteLine(JSONObject content) { private void doDeleteLine(JSONObject content) {
lines.remove(Line.fromJSONArray(content.getJSONArray("line"))); lines.remove(Line.fromJSONArray(content.getJSONArray("line")));
@ -409,10 +479,8 @@ public class HelloController implements Initializable {
} }
} }
}); });
for (Line line: lines) { for (Stroke stroke: importedLine) {
for (Stroke stroke: line) { stroke.draw(gc, stroke.getColor());
stroke.draw(gc, colorPicker.getValue());
}
} }
} }
} }

View file

@ -3,18 +3,18 @@ package fr.emiko.graphicsElement;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import java.util.UUID;
import java.util.Vector; import java.util.Vector;
public class Line extends Vector<Stroke> { public class Line extends Vector<Stroke> {
private int timestamp; private int timestamp;
private int layer;
private javafx.scene.paint.Color color;
public JSONObject toJSONObject() { public JSONObject toJSONObject() {
JSONArray jsonArray = new JSONArray(); JSONArray jsonArray = new JSONArray();
for (Stroke stroke: this) { for (Stroke stroke: this) {
jsonArray.put(stroke.toJSON()); jsonArray.put(stroke.toJSON());
} }
return new JSONObject().put("line", jsonArray); return new JSONObject().put("line", jsonArray).put("timestamp", timestamp);
} }
public static Line fromJSONArray(JSONArray jsonArray) { public static Line fromJSONArray(JSONArray jsonArray) {
@ -32,12 +32,4 @@ public class Line extends Vector<Stroke> {
public void setTimestamp(int timestamp) { public void setTimestamp(int timestamp) {
this.timestamp = timestamp; this.timestamp = timestamp;
} }
public int getLayer() {
return layer;
}
public void setLayer(int layer) {
this.layer = layer;
}
} }

View file

@ -35,7 +35,7 @@ public class Stroke {
jsonObject.getDouble("toX"), jsonObject.getDouble("toX"),
jsonObject.getDouble("toY"), jsonObject.getDouble("toY"),
jsonObject.getDouble("brushSize"), jsonObject.getDouble("brushSize"),
(Color) jsonObject.get("color") Color.valueOf(jsonObject.get("color").toString())
); );
} }

View file

@ -0,0 +1,34 @@
package fr.emiko.graphicsElement;
import javafx.geometry.Pos;
import javafx.scene.control.ListCell;
import javafx.scene.canvas.Canvas;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.text.Text;
public class layerListViewCell extends ListCell<Canvas> {
@Override
protected void updateItem(Canvas item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
} else {
updateItem(item);
}
}
private void updateItem(Canvas item) {
ImageView imageView = new ImageView();
imageView.setImage(item.snapshot(null, null));
imageView.setFitHeight(25);
imageView.setFitWidth(25);
Text text = new Text(item.toString());
HBox hbox = new HBox(imageView, text);
hbox.setSpacing(10);
hbox.setAlignment(Pos.CENTER_LEFT);
setGraphic(hbox);
}
}

View file

@ -17,6 +17,8 @@ public class DrawServer {
private ServerSocket passiveSocket; private ServerSocket passiveSocket;
private Vector<DrawClientHandler> clientList = new Vector<DrawClientHandler>(); private Vector<DrawClientHandler> clientList = new Vector<DrawClientHandler>();
private Vector<Line> lines; private Vector<Line> lines;
private double canvasWidth;
private double canvasHeight;
public DrawServer(int port) throws IOException { public DrawServer(int port) throws IOException {
passiveSocket = new ServerSocket(port); passiveSocket = new ServerSocket(port);
} }
@ -158,16 +160,26 @@ public class DrawServer {
doDelLine(event.getContent()); doDelLine(event.getContent());
return true; return true;
} }
case Event.LSTLINE -> { case Event.LINELST -> {
doSendLines(); doSendLines();
return true; return true;
} }
case Event.ADDCANVAS -> {
doAddCanvas(event.getContent());
return true;
}
default -> { default -> {
return false; return false;
} }
} }
} }
private void doAddCanvas(JSONObject content) throws JSONException {
canvasWidth = content.getDouble("width");
canvasHeight = content.getDouble("height");
sendAllOtherUsers(new Event(Event.CNVS, content));
}
private void doDelLine(JSONObject content) { private void doDelLine(JSONObject content) {
Line line = Line.fromJSONArray(content.getJSONArray("line")); Line line = Line.fromJSONArray(content.getJSONArray("line"));
this.user.getLines().remove(line); this.user.getLines().remove(line);
@ -189,9 +201,11 @@ public class DrawServer {
} }
private void sendAllOtherUsers(Event event) { private void sendAllOtherUsers(Event event) {
System.out.println("current user: " + this.user.getUsername());
for (DrawClientHandler client : clientList) { for (DrawClientHandler client : clientList) {
System.out.println("calculating user: " + client.user.getUsername());
if (client.user != this.user) { if (client.user != this.user) {
System.out.println(client.user.getUsername()); System.out.println("found user: " + client.user.getUsername());
sendEvent(client, event); sendEvent(client, event);
} }
} }
@ -203,6 +217,12 @@ public class DrawServer {
} }
private void doSendLines() { private void doSendLines() {
out.println(
new Event("CNVS", new JSONObject()
.put("width", canvasWidth)
.put("height", canvasHeight))
);
Vector<Line> lines = new Vector<>(); Vector<Line> lines = new Vector<>();
for (DrawClientHandler client: clientList) { for (DrawClientHandler client: clientList) {
for (Line line: client.user.getLines()) { for (Line line: client.user.getLines()) {
@ -210,7 +230,7 @@ public class DrawServer {
} }
} }
for (Line line: lines) { for (Line line: lines) {
out.println(new Event("LINELST", line.toJSONObject())); out.println(new Event("LINE", line.toJSONObject()));
} }
} }

View file

@ -9,6 +9,8 @@ public class Event {
public static final String DELLINE = "DELLINE"; public static final String DELLINE = "DELLINE";
public static final String LINE = "LINE"; public static final String LINE = "LINE";
public static final String LINELST = "LINELST"; public static final String LINELST = "LINELST";
public static final String ADDCANVAS = "ADDCANVAS";
public static final String CNVS = "CNVS";
private String type; private String type;
private JSONObject content; private JSONObject content;

View file

@ -50,23 +50,10 @@
</Menu> </Menu>
</menus> </menus>
</MenuBar> </MenuBar>
<SplitPane fx:id="mainPane" dividerPositions="0.16948784722222218"> <SplitPane fx:id="mainPane" dividerPositions="0.16948784722222218" VBox.vgrow="ALWAYS">
<items> <items>
<VBox prefHeight="200.0" prefWidth="100.0" spacing="10.0"> <VBox spacing="10.0">
<children> <children>
<GridPane>
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Label text="Zoom" />
<Slider fx:id="zoomSlider" blockIncrement="0.1" majorTickUnit="5.0" max="5.0" min="1.0" minorTickCount="10" showTickLabels="true" showTickMarks="true" snapToTicks="true" value="1.0" GridPane.rowIndex="1" />
</children>
</GridPane>
<GridPane> <GridPane>
<columnConstraints> <columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" /> <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" />
@ -117,7 +104,7 @@
<children> <children>
<Pane fx:id="pane"> <Pane fx:id="pane">
<children> <children>
<Canvas fx:id="drawingCanvas" height="600.0" nodeOrientation="INHERIT" translateX="10.0" translateY="10.0" width="800.0" /> <Canvas fx:id="drawingCanvas" height="1.0" nodeOrientation="INHERIT" translateX="10.0" translateY="10.0" width="1.0" />
</children> </children>
</Pane> </Pane>
</children> </children>
@ -129,7 +116,7 @@
<HBox> <HBox>
<children> <children>
<Label text="Status : " /> <Label text="Status : " />
<Label text="Connected to username@localhost:8090" /> <Label text="Disconnected" />
</children> </children>
<VBox.margin> <VBox.margin>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" /> <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />