diff --git a/resources/images/captor_icon.png b/resources/images/captor_icon.png new file mode 100644 index 0000000..6995fa2 Binary files /dev/null and b/resources/images/captor_icon.png differ diff --git a/resources/images/multi_captor_icon.png b/resources/images/multi_captor_icon.png new file mode 100644 index 0000000..1e9dc6b Binary files /dev/null and b/resources/images/multi_captor_icon.png differ diff --git a/resources/windows/MainWindows.fxml b/resources/windows/MainWindows.fxml index 736e5f3..9b73069 100644 --- a/resources/windows/MainWindows.fxml +++ b/resources/windows/MainWindows.fxml @@ -1,7 +1,6 @@ - @@ -13,10 +12,11 @@ +
- + @@ -34,6 +34,7 @@ + diff --git a/src/fr/uca/iut/clfreville2/gui/MainWindows.java b/src/fr/uca/iut/clfreville2/gui/MainWindows.java index 436bd66..cd32ddb 100644 --- a/src/fr/uca/iut/clfreville2/gui/MainWindows.java +++ b/src/fr/uca/iut/clfreville2/gui/MainWindows.java @@ -1,10 +1,12 @@ package fr.uca.iut.clfreville2.gui; import fr.uca.iut.clfreville2.gui.image.ImageSupplier; +import fr.uca.iut.clfreville2.gui.image.SensorTypeImageSupplier; import fr.uca.iut.clfreville2.gui.image.StandardImageSupplier; -import fr.uca.iut.clfreville2.gui.list.NameableListCell; import fr.uca.iut.clfreville2.gui.table.WeightSpinnerTableCell; import fr.uca.iut.clfreville2.gui.thread.Ticker; +import fr.uca.iut.clfreville2.gui.tree.SensorTreeCell; +import fr.uca.iut.clfreville2.gui.tree.SensorTreeItemBridge; import fr.uca.iut.clfreville2.model.SensorRegistry; import fr.uca.iut.clfreville2.model.binding.ToBooleanBinding; import fr.uca.iut.clfreville2.model.sensor.ManualSensor; @@ -16,12 +18,15 @@ import javafx.collections.FXCollections; import javafx.fxml.FXML; import javafx.scene.control.Button; import javafx.scene.control.CheckBox; -import javafx.scene.control.ListView; import javafx.scene.control.Slider; import javafx.scene.control.Spinner; +import javafx.scene.control.TableCell; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import javafx.scene.control.TextField; +import javafx.scene.control.TreeItem; +import javafx.scene.control.TreeView; +import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.image.ImageView; import javafx.scene.text.Text; import javafx.stage.Stage; @@ -30,11 +35,12 @@ public class MainWindows { private final SensorRegistry registry = new StubSensorRegistryLoader().load(); private final ImageSupplier imageSupplier = new StandardImageSupplier(); + private final ImageSupplier imageTypeSupplier = new SensorTypeImageSupplier(); private final Ticker ticker; private final ModalFactory modalFactory; @FXML - private ListView sensorsList; + private TreeView sensorTree; @FXML private Text sensorId; @@ -51,6 +57,9 @@ public class MainWindows { @FXML private TableView sourcesView; + @FXML + private TableColumn sourceIcon; + @FXML private TableColumn sourceWeight; @@ -105,26 +114,26 @@ public class MainWindows { @FXML private void initialize() { - bindSensorList(); + bindSensorTree(); bindActiveButtons(); bindSources(); bindUpdate(); } @FXML - private void bindSensorList() { - sensorsList.setCellFactory(list -> new NameableListCell<>()); - sensorsList.itemsProperty().bind(registry.sensorsProperty()); - sensorsList.getSelectionModel().selectedItemProperty().addListener((list, oldValue, newValue) -> { - if (oldValue != null) { - sensorName.textProperty().unbindBidirectional(oldValue.nameProperty()); + private void bindSensorTree() { + sensorTree.setCellFactory(tree -> new SensorTreeCell(imageTypeSupplier)); + sensorTree.setRoot(SensorTreeItemBridge.create(registry)); + sensorTree.getSelectionModel().selectedItemProperty().addListener((list, oldValue, newValue) -> { + if (oldValue != null && oldValue.getValue() != null) { + sensorName.textProperty().unbindBidirectional(oldValue.getValue().nameProperty()); sourcesView.itemsProperty().unbind(); sourcesView.setItems(FXCollections.emptyObservableList()); } - if (newValue != null) { - sensorId.textProperty().bind(newValue.displayNameExpression()); - sensorName.textProperty().bindBidirectional(newValue.nameProperty()); - if (newValue instanceof VirtualSensor virtual) { + if (newValue != null && newValue.getValue() != null) { + sensorId.textProperty().bind(newValue.getValue().displayNameExpression()); + sensorName.textProperty().bindBidirectional(newValue.getValue().nameProperty()); + if (newValue.getValue() instanceof VirtualSensor virtual) { sourcesView.itemsProperty().bind(virtual.sourcesProperty()); } } else { @@ -137,16 +146,28 @@ public class MainWindows { @FXML private void bindActiveButtons() { changeBtn.visibleProperty().bind(new ToBooleanBinding<>( - sensorsList.getSelectionModel().selectedItemProperty(), - treeItem -> treeItem instanceof ManualSensor + sensorTree.getSelectionModel().selectedItemProperty(), + treeItem -> treeItem != null && treeItem.getValue() instanceof ManualSensor )); - visualizeBtn.visibleProperty().bind(sensorsList.getSelectionModel().selectedItemProperty().isNotNull()); + visualizeBtn.visibleProperty().bind(sensorTree.getSelectionModel().selectedItemProperty().isNotNull()); } @FXML private void bindSources() { sourceWeight.setCellValueFactory(cell -> new SimpleObjectProperty<>(cell.getValue())); sourceWeight.setCellFactory(cell -> new WeightSpinnerTableCell()); + sourceIcon.setCellValueFactory(cell -> new SimpleObjectProperty<>(cell.getValue().sensor())); + sourceIcon.setCellFactory(cell -> new TableCell<>() { + @Override + protected void updateItem(Sensor item, boolean empty) { + super.updateItem(item, empty); + if (empty) { + setGraphic(null); + } else { + setGraphic(new ImageView(imageTypeSupplier.apply(item))); + } + } + }); sourceId.setCellValueFactory(cell -> cell.getValue().sensor().idProperty().asString()); } @@ -157,6 +178,10 @@ public class MainWindows { } private Sensor getSelectedSensor() { - return sensorsList.getSelectionModel().getSelectedItem(); + TreeItem selected = sensorTree.getSelectionModel().getSelectedItem(); + if (selected == null) { + return null; + } + return selected.getValue(); } } diff --git a/src/fr/uca/iut/clfreville2/gui/image/SensorTypeImageSupplier.java b/src/fr/uca/iut/clfreville2/gui/image/SensorTypeImageSupplier.java new file mode 100644 index 0000000..eb76350 --- /dev/null +++ b/src/fr/uca/iut/clfreville2/gui/image/SensorTypeImageSupplier.java @@ -0,0 +1,24 @@ +package fr.uca.iut.clfreville2.gui.image; + +import fr.uca.iut.clfreville2.model.sensor.Sensor; +import fr.uca.iut.clfreville2.model.sensor.VirtualSensor; +import javafx.scene.image.Image; + +public class SensorTypeImageSupplier implements ImageSupplier { + + private final Image simpleIcon; + private final Image multiIcon; + + public SensorTypeImageSupplier() { + simpleIcon = new Image("/images/captor_icon.png"); + multiIcon = new Image("/images/multi_captor_icon.png"); + } + + @Override + public Image apply(Sensor sensor) { + if (sensor instanceof VirtualSensor) { + return multiIcon; + } + return simpleIcon; + } +} diff --git a/src/fr/uca/iut/clfreville2/gui/tree/SensorTreeCell.java b/src/fr/uca/iut/clfreville2/gui/tree/SensorTreeCell.java new file mode 100644 index 0000000..bca915a --- /dev/null +++ b/src/fr/uca/iut/clfreville2/gui/tree/SensorTreeCell.java @@ -0,0 +1,31 @@ +package fr.uca.iut.clfreville2.gui.tree; + +import fr.uca.iut.clfreville2.gui.image.ImageSupplier; +import fr.uca.iut.clfreville2.model.sensor.Sensor; +import javafx.scene.control.TreeCell; +import javafx.scene.image.ImageView; + +public class SensorTreeCell extends TreeCell { + + private final ImageView imageView = new ImageView(); + + private final ImageSupplier imageSupplier; + + public SensorTreeCell(ImageSupplier imageSupplier) { + this.imageSupplier = imageSupplier; + } + + @Override + protected void updateItem(Sensor item, boolean empty) { + super.updateItem(item, empty); + if (empty || item == null) { + textProperty().unbind(); + textProperty().setValue(null); + setGraphic(null); + } else { + imageView.setImage(imageSupplier.apply(item)); + textProperty().bind(item.displayNameExpression()); + setGraphic(imageView); + } + } +} diff --git a/src/fr/uca/iut/clfreville2/gui/tree/SensorTreeItemBridge.java b/src/fr/uca/iut/clfreville2/gui/tree/SensorTreeItemBridge.java new file mode 100644 index 0000000..8e6e000 --- /dev/null +++ b/src/fr/uca/iut/clfreville2/gui/tree/SensorTreeItemBridge.java @@ -0,0 +1,82 @@ +package fr.uca.iut.clfreville2.gui.tree; + +import fr.uca.iut.clfreville2.model.SensorRegistry; +import fr.uca.iut.clfreville2.model.sensor.Sensor; +import fr.uca.iut.clfreville2.model.sensor.VirtualSensor; +import javafx.collections.ListChangeListener; +import javafx.scene.control.TreeItem; + +public final class SensorTreeItemBridge { + + private SensorTreeItemBridge() {} + + public static TreeItem create(Sensor sensor) { + TreeItem item = new TreeItem<>(sensor); + if (sensor instanceof VirtualSensor virtual) { + addChildren(virtual.sourcesProperty(), item); + virtual.sourcesProperty().addListener(new ListChangeListener() { + @Override + public void onChanged(Change change) { + while (change.next()) { + if (change.wasPermutated()) { + throw new UnsupportedOperationException(); + } else if (change.wasUpdated()) { + throw new UnsupportedOperationException(); + } else { + addChildren(change.getAddedSubList(), item); + removeChildren(change.getRemoved(), item); + } + } + } + }); + } + return item; + } + + public static TreeItem create(SensorRegistry registry) { + TreeItem root = new TreeItem<>(); + for (Sensor sensor : registry) { + root.getChildren().add(create(sensor)); + } + registry.sensorsProperty().addListener(new ListChangeListener() { + @Override + public void onChanged(Change change) { + while (change.next()) { + if (change.wasPermutated()) { + throw new UnsupportedOperationException(); + } else if (change.wasUpdated()) { + throw new UnsupportedOperationException(); + } else { + addSensors(change.getAddedSubList(), root); + removeSensors(change.getRemoved(), root); + } + } + } + }); + return root; + } + + private static void addSensors(Iterable sensors, TreeItem item) { + for (Sensor sensor : sensors) { + item.getChildren().add(create(sensor)); + } + } + + private static void addChildren(Iterable sources, TreeItem item) { + for (VirtualSensor.DataSource source : sources) { + item.getChildren().add(create(source.sensor())); + } + } + + private static void removeChildren(Iterable sources, TreeItem item) { + for (VirtualSensor.DataSource source : sources) { + item.getChildren().removeIf(child -> child.getValue().equals(source.sensor())); + } + } + + private static void removeSensors(Iterable sensors, TreeItem item) { + for (Sensor sensor : sensors) { + item.getChildren().removeIf(child -> child.getValue().equals(sensor)); + } + } +}