I had accidentally broken the raceXML construction - fixed it.
Fixed a few issues where boat didn't have nextMark/previousMark times under certain circumstances. LatestMessages wasn't actually notifying properly - fixed. The various XML readers now treat a string constructor as containing file contents, not file name - this was how it was mostly already used. Fixed some issues in RaceXMLReader, including where element.getChildNodes() was used instead of element.getElementsByName(...). Boat: changes speed to a doubleProperty. Changed leg to a Property<Leg>. Race.lastFps is now an IntegerProperty. Added breaks to the case statements in VisualiserInput. Whoops. ResizableRaceCanvas now handles drawing the race boundary. Tidied up code a bit. Removed ResizableRaceMap. Started refactoring Sparkline. Currently doesn't work.
This commit is contained in:
@@ -7,14 +7,14 @@ import javafx.scene.control.Label;
|
||||
import javafx.scene.control.TableColumn;
|
||||
import javafx.scene.control.TableView;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import seng302.Model.Boat;
|
||||
import visualiser.model.VisualiserBoat;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
|
||||
/**
|
||||
* Finish Screen for when the race finishs.
|
||||
* Finish Screen for when the race finishes.
|
||||
*/
|
||||
public class FinishController extends Controller {
|
||||
|
||||
@@ -22,29 +22,28 @@ public class FinishController extends Controller {
|
||||
AnchorPane finishWrapper;
|
||||
|
||||
@FXML
|
||||
TableView<Boat> boatInfoTable;
|
||||
TableView<VisualiserBoat> boatInfoTable;
|
||||
|
||||
@FXML
|
||||
TableColumn<Boat, String> boatRankColumn;
|
||||
TableColumn<VisualiserBoat, String> boatRankColumn;
|
||||
|
||||
@FXML
|
||||
TableColumn<Boat, String> boatNameColumn;
|
||||
TableColumn<VisualiserBoat, String> boatNameColumn;
|
||||
|
||||
@FXML
|
||||
Label raceWinnerLabel;
|
||||
|
||||
|
||||
/**
|
||||
* Sets up the finish table
|
||||
* @param boats Boats to display
|
||||
* The boats to display on the table.
|
||||
*/
|
||||
private void setFinishTable(ObservableList<Boat> boats){
|
||||
boatInfoTable.setItems(boats);
|
||||
boatNameColumn.setCellValueFactory(cellData -> cellData.getValue().getName());
|
||||
boatRankColumn.setCellValueFactory(cellData -> cellData.getValue().positionProperty());
|
||||
private ObservableList<VisualiserBoat> boats;
|
||||
|
||||
raceWinnerLabel.setText("Winner: "+ boatNameColumn.getCellObservableValue(0).getValue());
|
||||
raceWinnerLabel.setWrapText(true);
|
||||
|
||||
/**
|
||||
* Ctor.
|
||||
*/
|
||||
public FinishController() {
|
||||
}
|
||||
|
||||
|
||||
@@ -52,11 +51,38 @@ public class FinishController extends Controller {
|
||||
public void initialize(URL location, ResourceBundle resources){
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Sets up the finish table
|
||||
* @param boats Boats to display
|
||||
*/
|
||||
private void setFinishTable(ObservableList<VisualiserBoat> boats) {
|
||||
this.boats = boats;
|
||||
|
||||
//Set contents.
|
||||
boatInfoTable.setItems(boats);
|
||||
|
||||
//Name.
|
||||
boatNameColumn.setCellValueFactory(cellData -> cellData.getValue().nameProperty());
|
||||
|
||||
//Rank/position.
|
||||
boatRankColumn.setCellValueFactory(cellData -> cellData.getValue().positionProperty());
|
||||
|
||||
|
||||
//Winner label.
|
||||
raceWinnerLabel.setText("Winner: "+ boatNameColumn.getCellObservableValue(0).getValue());
|
||||
raceWinnerLabel.setWrapText(true);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Display the table
|
||||
* @param boats boats to display on the table.
|
||||
*/
|
||||
public void enterFinish(ObservableList<Boat> boats){
|
||||
public void enterFinish(ObservableList<VisualiserBoat> boats){
|
||||
finishWrapper.setVisible(true);
|
||||
setFinishTable(boats);
|
||||
}
|
||||
|
||||
@@ -15,11 +15,22 @@ import java.util.ResourceBundle;
|
||||
* Controller that everything is overlayed onto. This makes it so that changing scenes does not resize your stage.
|
||||
*/
|
||||
public class MainController extends Controller {
|
||||
|
||||
@FXML private StartController startController;
|
||||
@FXML private RaceController raceController;
|
||||
@FXML private ConnectionController connectionController;
|
||||
@FXML private FinishController finishController;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Ctor.
|
||||
*/
|
||||
public MainController() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Transitions from the StartController screen (displays pre-race information) to the RaceController (displays the actual race).
|
||||
* @param visualiserInput The object used to read packets from the race server.
|
||||
@@ -29,10 +40,18 @@ public class MainController extends Controller {
|
||||
raceController.startRace(visualiserInput, visualiserRace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transitions from the server selection screen to the pre-race lobby for a given server.
|
||||
* @param socket The server to read data from.
|
||||
*/
|
||||
public void enterLobby(Socket socket) {
|
||||
startController.enterLobby(socket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transitions from the {@link RaceController} screen to the {@link FinishController} screen.
|
||||
* @param boats The boats to display on the finish screen.
|
||||
*/
|
||||
public void enterFinish(ObservableList<VisualiserBoat> boats) {
|
||||
finishController.enterFinish(boats);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package visualiser.Controllers;
|
||||
|
||||
|
||||
import javafx.animation.AnimationTimer;
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.chart.LineChart;
|
||||
@@ -10,13 +12,14 @@ import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.util.Callback;
|
||||
import network.Messages.Enums.RaceStatusEnum;
|
||||
import shared.model.Leg;
|
||||
import visualiser.app.VisualiserInput;
|
||||
import shared.model.RaceClock;
|
||||
import visualiser.model.Sparkline;
|
||||
import visualiser.model.VisualiserBoat;
|
||||
import visualiser.model.VisualiserRace;
|
||||
import visualiser.model.*;
|
||||
|
||||
import java.net.URL;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
/**
|
||||
@@ -34,8 +37,15 @@ public class RaceController extends Controller {
|
||||
*/
|
||||
private VisualiserRace visualiserRace;
|
||||
|
||||
private ResizableRaceCanvas raceMap;
|
||||
private ResizableRaceMap raceBoundaries;
|
||||
/**
|
||||
* An additional observable list of boats. This is used by the table view, to allow it to sort boats without effecting the race's own list of boats.
|
||||
*/
|
||||
private ObservableList<VisualiserBoat> tableBoatList;
|
||||
|
||||
/**
|
||||
* The canvas that draws the race.
|
||||
*/
|
||||
private ResizableRaceCanvas raceCanvas;
|
||||
|
||||
/**
|
||||
* The sparkline graph.
|
||||
@@ -54,12 +64,13 @@ public class RaceController extends Controller {
|
||||
@FXML private TableView<VisualiserBoat> boatInfoTable;
|
||||
@FXML private TableColumn<VisualiserBoat, String> boatPlacingColumn;
|
||||
@FXML private TableColumn<VisualiserBoat, String> boatTeamColumn;
|
||||
@FXML private TableColumn<VisualiserBoat, String> boatMarkColumn;
|
||||
@FXML private TableColumn<VisualiserBoat, String> boatSpeedColumn;
|
||||
@FXML private TableColumn<VisualiserBoat, Leg> boatMarkColumn;
|
||||
@FXML private TableColumn<VisualiserBoat, Number> boatSpeedColumn;
|
||||
@FXML private LineChart<Number, Number> sparklineChart;
|
||||
@FXML private AnchorPane annotationPane;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Ctor.
|
||||
*/
|
||||
@@ -68,10 +79,54 @@ public class RaceController extends Controller {
|
||||
|
||||
@Override
|
||||
public void initialize(URL location, ResourceBundle resources) {
|
||||
initialiseFpsToggle();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialises the various UI components to listen to the {@link #visualiserRace}.
|
||||
*/
|
||||
private void initialiseRace() {
|
||||
//Fps display.
|
||||
initialiseFps(this.visualiserRace);
|
||||
|
||||
//Need to add the included arrow pane to the arrowPane container.
|
||||
initialiseArrow();
|
||||
|
||||
//Information table.
|
||||
initialiseInfoTable(this.visualiserRace);
|
||||
|
||||
//Sparkline.
|
||||
initialiseSparkline(this.visualiserRace);
|
||||
|
||||
//Race canvas.
|
||||
initialiseRaceCanvas(this.visualiserRace);
|
||||
|
||||
//Race timezone label.
|
||||
initialiseRaceTimezoneLabel(this.visualiserRace);
|
||||
|
||||
//Race clock.
|
||||
initialiseRaceClock(this.visualiserRace);
|
||||
|
||||
|
||||
//Start the race animation timer.
|
||||
raceTimer();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Initialises the frame rate functionality. This allows for toggling the frame rate, and connect the fps label to the race's fps property.
|
||||
* @param visualiserRace The race to connect the fps label to.
|
||||
*/
|
||||
private void initialiseFps(VisualiserRace visualiserRace) {
|
||||
|
||||
//On/off toggle.
|
||||
initialiseFpsToggle();
|
||||
|
||||
//Label value.
|
||||
initialiseFpsLabel(visualiserRace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises a listener for the fps toggle.
|
||||
*/
|
||||
@@ -90,53 +145,167 @@ public class RaceController extends Controller {
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the ResizableRaceCanvas (raceMap) with most recent data
|
||||
*
|
||||
* @param boats boats that are to be displayed in the race
|
||||
* @param boatMarkers Markers for boats
|
||||
* @see ResizableRaceCanvas
|
||||
* Initialises the fps label to update when the race fps changes.
|
||||
* @param visualiserRace The race to monitor the frame rate of.
|
||||
*/
|
||||
public void updateMap(ObservableList<Boat> boats, ObservableList<Marker> boatMarkers) {
|
||||
raceMap.setBoats(boats);
|
||||
raceMap.setBoatMarkers(boatMarkers);
|
||||
raceMap.update();
|
||||
raceBoundaries.draw();
|
||||
//stop if the visualiser is no longer running
|
||||
private void initialiseFpsLabel(VisualiserRace visualiserRace) {
|
||||
|
||||
visualiserRace.fpsProperty().addListener((observable, oldValue, newValue) -> {
|
||||
Platform.runLater(() -> this.FPS.setText("FPS: " + newValue.toString()));
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Updates the array listened by the TableView (boatInfoTable) that displays the boat information.
|
||||
*
|
||||
* Initialises the information table view to listen to a given race.
|
||||
* @param race Race to listen to.
|
||||
*/
|
||||
public void setInfoTable(VisualiserRace race) {
|
||||
boatInfoTable.setItems(race.getStartingBoats());
|
||||
boatTeamColumn.setCellValueFactory(cellData -> cellData.getValue().getName());
|
||||
boatSpeedColumn.setCellValueFactory(cellData -> cellData.getValue().getVelocityProp());
|
||||
boatMarkColumn.setCellValueFactory(cellData -> cellData.getValue().getCurrentLegName());
|
||||
boatPlacingColumn.setCellValueFactory(cellData -> cellData.getValue().positionProperty());
|
||||
public void initialiseInfoTable(VisualiserRace race) {
|
||||
|
||||
//Copy list of boats.
|
||||
this.tableBoatList = FXCollections.observableArrayList(race.getBoats());
|
||||
|
||||
|
||||
//Set up table.
|
||||
boatInfoTable.setItems(this.tableBoatList);
|
||||
|
||||
|
||||
//Set up each column.
|
||||
|
||||
//Name.
|
||||
boatTeamColumn.setCellValueFactory(
|
||||
cellData -> cellData.getValue().nameProperty() );
|
||||
|
||||
//Speed.
|
||||
boatSpeedColumn.setCellValueFactory(
|
||||
cellData -> cellData.getValue().currentSpeedProperty() );
|
||||
|
||||
//Kind of ugly, but allows for formatting an observed speed.
|
||||
boatSpeedColumn.setCellFactory(
|
||||
//Callback object.
|
||||
new Callback<TableColumn<VisualiserBoat, Number>, TableCell<VisualiserBoat, Number>>() {
|
||||
|
||||
//Callback function.
|
||||
@Override
|
||||
public TableCell<VisualiserBoat, Number> call(TableColumn<VisualiserBoat, Number> param) {
|
||||
//We return a table cell that populates itself with a Number, and formats it.
|
||||
return new TableCell<VisualiserBoat, Number>(){
|
||||
|
||||
//Function to update the cell text.
|
||||
@Override
|
||||
protected void updateItem(Number item, boolean empty) {
|
||||
|
||||
if (item != null) {
|
||||
super.updateItem(item, empty);
|
||||
|
||||
setText(String.format("%.2fkn", item.doubleValue()));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
|
||||
//Last mark.
|
||||
boatMarkColumn.setCellValueFactory(
|
||||
cellData -> cellData.getValue().legProperty() );
|
||||
|
||||
//Kind of ugly, but allows for turning an observed Leg into a string.
|
||||
boatMarkColumn.setCellFactory(
|
||||
//Callback object.
|
||||
new Callback<TableColumn<VisualiserBoat, Leg>, TableCell<VisualiserBoat, Leg>>() {
|
||||
|
||||
//Callback function.
|
||||
@Override
|
||||
public TableCell<VisualiserBoat, Leg> call(TableColumn<VisualiserBoat, Leg> param) {
|
||||
//We return a table cell that populates itself with a Leg's name.
|
||||
return new TableCell<VisualiserBoat, Leg>(){
|
||||
|
||||
//Function to update the cell text.
|
||||
@Override
|
||||
protected void updateItem(Leg item, boolean empty) {
|
||||
|
||||
if (item != null) {
|
||||
super.updateItem(item, empty);
|
||||
|
||||
setText(item.getName());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
//Current place within race.
|
||||
boatPlacingColumn.setCellValueFactory(
|
||||
cellData -> cellData.getValue().positionProperty() );
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates and sets initial display for Sparkline for race positions.
|
||||
* @param boats boats to display on the sparkline
|
||||
* Initialises the {@link Sparkline}, and listens to a specified {@link VisualiserRace}.
|
||||
* @param race The race to listen to.
|
||||
*/
|
||||
public void createSparkLine(ObservableList<VisualiserBoat> boats){
|
||||
sparkline = new Sparkline(boats, legNum, sparklineChart);
|
||||
private void initialiseSparkline(VisualiserRace race) {
|
||||
this.sparkline = new Sparkline(race.getBoats(), race.getLegCount(), this.sparklineChart);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialises the {@link ResizableRaceCanvas}, provides the race to read data from.
|
||||
* @param race Race to read data from.
|
||||
*/
|
||||
private void initialiseRaceCanvas(VisualiserRace race) {
|
||||
|
||||
//Create canvas.
|
||||
raceCanvas = new ResizableRaceCanvas(race, arrow.getChildren().get(0));
|
||||
|
||||
//Set properties.
|
||||
raceCanvas.setMouseTransparent(true);
|
||||
raceCanvas.widthProperty().bind(canvasBase.widthProperty());
|
||||
raceCanvas.heightProperty().bind(canvasBase.heightProperty());
|
||||
|
||||
//Draw it and show it.
|
||||
raceCanvas.draw();
|
||||
raceCanvas.setVisible(true);
|
||||
|
||||
//Add to scene.
|
||||
canvasBase.getChildren().add(0, raceCanvas);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Intialises the race time zone label with the race's time zone.
|
||||
* @param race The race to get time zone from.
|
||||
*/
|
||||
private void initialiseRaceTimezoneLabel(VisualiserRace race) {
|
||||
timeZone.setText(race.getRaceClock().getTimeZone());
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the sparkline to display current boat positions.
|
||||
* @param boatsInRace used for current boat positions.
|
||||
* Initialises the race clock to listen to the specified race.
|
||||
* @param race The race to listen to.
|
||||
*/
|
||||
public void updateSparkline(ObservableList<VisualiserBoat> boatsInRace){
|
||||
sparkline.updateSparkline(boatsInRace);
|
||||
private void initialiseRaceClock(VisualiserRace race) {
|
||||
|
||||
//RaceClock.duration isn't necessarily being changed in the javaFX thread, so we need to runlater the update.
|
||||
race.getRaceClock().durationProperty().addListener((observable, oldValue, newValue) -> {
|
||||
Platform.runLater(() -> {
|
||||
timer.setText(newValue);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Displays a specified race.
|
||||
* @param visualiserInput Object used to read packets from server.
|
||||
@@ -147,92 +316,69 @@ public class RaceController extends Controller {
|
||||
this.visualiserInput = visualiserInput;
|
||||
this.visualiserRace = visualiserRace;
|
||||
|
||||
legNum = visualiserInput.getCourse().getLegs().size()-1;
|
||||
|
||||
makeArrow();
|
||||
|
||||
raceMap = new ResizableRaceCanvas(visualiserInput.getCourse());
|
||||
raceMap.setMouseTransparent(true);
|
||||
raceMap.widthProperty().bind(canvasBase.widthProperty());
|
||||
raceMap.heightProperty().bind(canvasBase.heightProperty());
|
||||
raceMap.draw();
|
||||
raceMap.setVisible(true);
|
||||
raceMap.setArrow(arrow.getChildren().get(0));
|
||||
|
||||
canvasBase.getChildren().add(0, raceMap);
|
||||
|
||||
raceBoundaries = new ResizableRaceMap(visualiserInput.getCourse());
|
||||
raceBoundaries.setMouseTransparent(true);
|
||||
raceBoundaries.widthProperty().bind(canvasBase.widthProperty());
|
||||
raceBoundaries.heightProperty().bind(canvasBase.heightProperty());
|
||||
raceBoundaries.draw();
|
||||
raceBoundaries.setVisible(true);
|
||||
|
||||
canvasBase.getChildren().add(0, raceBoundaries);
|
||||
initialiseRace();
|
||||
|
||||
//Display this controller.
|
||||
race.setVisible(true);
|
||||
|
||||
timeZone.setText(raceClock.getTimeZone());
|
||||
|
||||
//RaceClock.duration isn't necessarily being changed in the javaFX thread, so we need to runlater the update.
|
||||
raceClock.durationProperty().addListener((observable, oldValue, newValue) -> {
|
||||
Platform.runLater(() -> {
|
||||
timer.setText(newValue);
|
||||
});
|
||||
});
|
||||
|
||||
this.raceClock = raceClock;
|
||||
raceMap.setRaceClock(raceClock);
|
||||
|
||||
//TODO move this list of colors somewhere more sensible.
|
||||
|
||||
StreamedRace newRace = new StreamedRace(visualiserInput, colours, this);
|
||||
|
||||
initializeFPS();
|
||||
|
||||
// set up annotation displays
|
||||
new Annotations(annotationPane, raceMap);
|
||||
|
||||
new Thread((newRace)).start();
|
||||
new Annotations(annotationPane, raceCanvas);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finish Race View
|
||||
* Transition from the race view to the finish view.
|
||||
* @param boats boats there are in the race.
|
||||
*/
|
||||
public void finishRace(ObservableList<Boat> boats){
|
||||
public void finishRace(ObservableList<VisualiserBoat> boats){
|
||||
race.setVisible(false);
|
||||
parent.enterFinish(boats);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value for the fps label
|
||||
*
|
||||
* @param fps fps that the label will be updated to
|
||||
*/
|
||||
public void setFrames(String fps) {
|
||||
FPS.setText((fps));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up FPS display at bottom of screen
|
||||
* Adds the included arrow pane (see arrow.fxml) to the arrowPane (see race.fxml).
|
||||
*/
|
||||
private void initializeFPS() {
|
||||
showFPS.setVisible(true);
|
||||
showFPS.selectedProperty().addListener((ov, old_val, new_val) -> {
|
||||
if (showFPS.isSelected()) {
|
||||
FPS.setVisible(true);
|
||||
} else {
|
||||
FPS.setVisible(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void makeArrow() {
|
||||
private void initialiseArrow() {
|
||||
arrowPane.getChildren().add(arrow);
|
||||
}
|
||||
|
||||
public RaceClock getRaceClock() {
|
||||
return raceClock;
|
||||
|
||||
|
||||
/**
|
||||
* Timer which monitors the race.
|
||||
*/
|
||||
private void raceTimer() {
|
||||
new AnimationTimer() {
|
||||
@Override
|
||||
public void handle(long arg0) {
|
||||
|
||||
//Get the current race status.
|
||||
RaceStatusEnum raceStatus = visualiserRace.getRaceStatusEnum();
|
||||
|
||||
|
||||
//If the race has finished, go to finish view.
|
||||
if (raceStatus == RaceStatusEnum.FINISHED) {
|
||||
//Stop this timer.
|
||||
stop();
|
||||
|
||||
//Hide this, and display the finish controller.
|
||||
finishRace(visualiserRace.getBoats());
|
||||
|
||||
} else {
|
||||
//Otherwise, render the canvas.
|
||||
raceCanvas.drawRace();
|
||||
|
||||
|
||||
//Sort the tableview. Doesn't automatically work for all columns.
|
||||
boatInfoTable.sort();
|
||||
|
||||
//Update sparkline. TODO this should simply observe the boats.
|
||||
sparkline.updateSparkline(visualiserRace.getBoats());
|
||||
}
|
||||
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -121,6 +121,7 @@ public class StartController extends Controller implements Observer {
|
||||
|
||||
//Create race.
|
||||
this.visualiserRace = new VisualiserRace(boatDataSource, raceDataSource, regattaDataSource, latestMessages, this.colors);
|
||||
new Thread(this.visualiserRace).start();
|
||||
|
||||
|
||||
//Initialise the boat table.
|
||||
@@ -193,6 +194,8 @@ public class StartController extends Controller implements Observer {
|
||||
*/
|
||||
private void initialiseRaceClockStartTime(VisualiserRace visualiserRace) {
|
||||
|
||||
raceStartLabel.setText(visualiserRace.getRaceClock().getStartingTimeString());
|
||||
|
||||
visualiserRace.getRaceClock().startingTimeProperty().addListener((observable, oldValue, newValue) -> {
|
||||
Platform.runLater(() -> {
|
||||
raceStartLabel.setText(newValue);
|
||||
|
||||
Reference in New Issue
Block a user