-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
- Loading branch information
Showing
10 changed files
with
398 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
114 changes: 114 additions & 0 deletions
114
org.mbari.vars.ui/src/main/java/org/mbari/vars/ui/javafx/timeline/DisplayedAnnotation.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
package org.mbari.vars.ui.javafx.timeline; | ||
|
||
import javafx.application.Platform; | ||
import javafx.beans.binding.DoubleBinding; | ||
import javafx.scene.control.Label; | ||
import javafx.scene.control.Tooltip; | ||
import javafx.scene.layout.Pane; | ||
import javafx.scene.paint.Color; | ||
import javafx.scene.shape.Line; | ||
import org.mbari.vars.core.EventBus; | ||
import org.mbari.vars.services.model.Annotation; | ||
import org.mbari.vars.ui.events.AnnotationsSelectedEvent; | ||
import org.mbari.vars.ui.util.ColorUtil; | ||
|
||
import java.util.List; | ||
|
||
record DisplayedAnnotation(Annotation annotation, Label label, Line line) { | ||
|
||
|
||
public void addTo(Pane parent, | ||
Line horizontalAxis, | ||
DoubleBinding distanceBetweenMinutes, | ||
EventBus eventBus) { | ||
if (annotation.getElapsedTime() == null) { | ||
return; | ||
} | ||
|
||
Platform.runLater(() -> { | ||
var minutes = annotation.getElapsedTime().toMillis() / 1000.0 / 60.0; | ||
var xProp = distanceBetweenMinutes.multiply(minutes).add(TimelineController.OFFSET); | ||
|
||
// --- LABEL | ||
var firstLetter = annotation.getConcept().toUpperCase().charAt(0); | ||
var charCode = (int) firstLetter; | ||
var shortName = firstLetter + ""; | ||
|
||
label.getStylesheets().clear(); | ||
label.setText(shortName); | ||
label.layoutXProperty().bind(xProp.subtract(label.widthProperty().divide(2))); | ||
var incremProp = parent.heightProperty() | ||
.subtract(TimelineController.OFFSET * 2) | ||
.subtract(horizontalAxis.startYProperty()) | ||
.divide(26) | ||
.multiply(charCode - 65) | ||
.add(horizontalAxis.startYProperty()); | ||
label.layoutYProperty().bind(incremProp); | ||
|
||
var fillHex = ColorUtil.stringToHexColor(annotation.getConcept(), 0.7); | ||
var fill = Color.web(fillHex, 0.7); | ||
// label.setTextFill(fill); | ||
|
||
|
||
label.setStyle("-fx-font-weight: bold; -fx-font-size: 16px; -fx-text-fill: " + fillHex + ";"); | ||
|
||
|
||
label.setOnMouseClicked(evt -> { | ||
var e = new AnnotationsSelectedEvent(DisplayedAnnotation.class, List.of(annotation)); | ||
eventBus.send(e); | ||
}); | ||
|
||
if (annotation.getRecordedTimestamp() != null) { | ||
label.setTooltip(new Tooltip(annotation.getConcept() + " at " + annotation.getRecordedTimestamp())); | ||
} | ||
else { | ||
label.setTooltip(new Tooltip(annotation.getConcept() + " at " + minutes + " minutes")); | ||
} | ||
|
||
// -- LINE | ||
line.startXProperty().bind(xProp); | ||
line.endXProperty().bind(xProp); | ||
line.startYProperty().bind(horizontalAxis.startYProperty()); | ||
line.endYProperty().bind(label.layoutYProperty()); | ||
|
||
var lightStroke = new Color(fill.getRed(), fill.getGreen(), fill.getBlue(), 0.15); | ||
var heavyStroke = new Color(fill.getRed(), fill.getGreen(), fill.getBlue(), 1); | ||
var heavyStrokeHex = ColorUtil.toHex(heavyStroke); | ||
line.setStroke(lightStroke); | ||
|
||
label.setOnMouseEntered(evt -> Platform.runLater(() -> { | ||
line.setStroke(heavyStroke); | ||
line.setStrokeWidth(3); | ||
label.setStyle("-fx-font-weight: bold; -fx-font-size: 18px; -fx-text-fill: " + heavyStrokeHex + ";"); | ||
label.setText(annotation.getConcept()); | ||
})); | ||
|
||
label.setOnMouseExited(evt -> Platform.runLater(() -> { | ||
line.setStroke(lightStroke); | ||
line.setStrokeWidth(3); | ||
label.setStyle("-fx-font-weight: bold; -fx-font-size: 16px; -fx-text-fill: " + fillHex + ";"); | ||
label.setText(shortName); | ||
})); | ||
|
||
parent.getChildren().addAll(line, label); | ||
}); | ||
|
||
} | ||
|
||
public void removeFrom(Pane parent) { | ||
Platform.runLater(() -> { | ||
label.layoutXProperty().unbind(); | ||
label.layoutYProperty().unbind(); | ||
line.startXProperty().unbind(); | ||
line.startYProperty().unbind(); | ||
line.endXProperty().unbind(); | ||
line.endYProperty().unbind(); | ||
label.setOnMouseEntered(e -> {}); | ||
label.setOnMouseExited(e -> {}); | ||
label.setOnMouseClicked(e -> {}); | ||
parent.getChildren().removeAll(label, line); | ||
parent.requestLayout(); | ||
}); | ||
|
||
} | ||
} |
43 changes: 43 additions & 0 deletions
43
org.mbari.vars.ui/src/main/java/org/mbari/vars/ui/javafx/timeline/TickMark.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package org.mbari.vars.ui.javafx.timeline; | ||
|
||
import javafx.application.Platform; | ||
import javafx.beans.binding.DoubleBinding; | ||
import javafx.scene.control.Label; | ||
import javafx.scene.layout.Pane; | ||
import javafx.scene.paint.Color; | ||
import javafx.scene.shape.Line; | ||
|
||
record TickMark(int minute, Line tick, Label label) { | ||
|
||
public void addTo(Pane parent, Line horizontalAxis, DoubleBinding distanceBetweenMinutes) { | ||
Platform.runLater(() -> { | ||
var offset = TimelineController.OFFSET; | ||
tick.startXProperty().bind(distanceBetweenMinutes.multiply(minute).add(offset)); | ||
tick.endXProperty().bind(tick.startXProperty()); | ||
|
||
tick.startYProperty().bind(horizontalAxis.startYProperty().subtract(offset)); | ||
tick.endYProperty().bind(horizontalAxis.startYProperty().add(offset)); | ||
tick.setStyle("-fx-stroke: #B3A9A3; -fx-stroke-width: 2px;"); | ||
|
||
var label = new Label("" + minute); | ||
label.setTextFill(Color.valueOf("#B3A9A3")); | ||
label.layoutXProperty().bind(tick.startXProperty().subtract(label.widthProperty().divide(2))); | ||
label.layoutYProperty().bind(tick.startYProperty().subtract(offset * 2)); | ||
label.setStyle("-fx-font-weight: bold; -fx-font-size: 18px;"); | ||
parent.getChildren().addAll(tick, label); | ||
}); | ||
} | ||
|
||
public void removeFrom(Pane parent) { | ||
Platform.runLater(() -> { | ||
parent.getChildren().removeAll(tick, label); | ||
tick.startXProperty().unbind(); | ||
tick.endXProperty().unbind(); | ||
tick.startYProperty().unbind(); | ||
tick.endYProperty().unbind(); | ||
label.layoutXProperty().unbind(); | ||
label.layoutYProperty().unbind(); | ||
}); | ||
|
||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
org.mbari.vars.ui/src/main/java/org/mbari/vars/ui/javafx/timeline/Timeline.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package org.mbari.vars.ui.javafx.timeline; | ||
|
||
import javafx.beans.binding.DoubleBinding; | ||
import javafx.scene.layout.Pane; | ||
import javafx.scene.shape.Line; | ||
import org.mbari.vars.services.model.Media; | ||
|
||
import java.util.List; | ||
|
||
record Timeline(Media media, List<TickMark> tickMarks) { | ||
public void addTo(Pane parent, Line horizontalAxis, DoubleBinding distanceBetweenMinutes) { | ||
tickMarks.forEach(t -> t.addTo(parent, horizontalAxis, distanceBetweenMinutes)); | ||
} | ||
|
||
public void removeFrom(Pane parent) { | ||
tickMarks.forEach(t -> t.removeFrom(parent)); | ||
} | ||
} |
122 changes: 122 additions & 0 deletions
122
org.mbari.vars.ui/src/main/java/org/mbari/vars/ui/javafx/timeline/TimelineController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
package org.mbari.vars.ui.javafx.timeline; | ||
|
||
import javafx.application.Platform; | ||
import javafx.beans.binding.DoubleBinding; | ||
import javafx.beans.property.DoubleProperty; | ||
import javafx.beans.property.SimpleDoubleProperty; | ||
import javafx.collections.FXCollections; | ||
import javafx.collections.ListChangeListener; | ||
import javafx.collections.ObservableList; | ||
import javafx.scene.control.Label; | ||
import javafx.scene.layout.Pane; | ||
import javafx.scene.shape.Line; | ||
import org.mbari.vars.services.model.Annotation; | ||
import org.mbari.vars.services.model.Media; | ||
import org.mbari.vars.ui.UIToolBox; | ||
import org.mbari.vars.ui.events.AnnotationsAddedEvent; | ||
import org.mbari.vars.ui.events.AnnotationsChangedEvent; | ||
import org.mbari.vars.ui.events.AnnotationsRemovedEvent; | ||
import org.mbari.vars.ui.events.MediaChangedEvent; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
public class TimelineController { | ||
|
||
public static final int OFFSET = 10; | ||
|
||
|
||
private final UIToolBox toolBox; | ||
private Pane root; | ||
DoubleProperty endXProperty = new SimpleDoubleProperty(); | ||
DoubleProperty numberOfMinutesProperty = new SimpleDoubleProperty(); | ||
DoubleBinding distanceBetweenMinutesProperty; | ||
Line horizontalAxis; | ||
List<DisplayedAnnotation> displayedAnno = new ArrayList<>(); | ||
private Timeline timeline; | ||
|
||
public TimelineController(UIToolBox toolBox) { | ||
this.toolBox = toolBox; | ||
init(); | ||
var eventBus = toolBox.getEventBus().toObserverable(); | ||
|
||
eventBus.ofType(MediaChangedEvent.class) | ||
.subscribe(e -> setMedia(e.get())); | ||
|
||
eventBus.ofType(AnnotationsAddedEvent.class) | ||
.subscribe(e -> e.get().forEach(this::addAnnotation)); | ||
|
||
eventBus.ofType(AnnotationsRemovedEvent.class) | ||
.subscribe(e -> e.get().forEach(this::removeAnnotation)); | ||
|
||
eventBus.ofType(AnnotationsChangedEvent.class) | ||
.subscribe(e -> e.get().forEach(a -> { | ||
removeAnnotation(a); | ||
addAnnotation(a); | ||
})); | ||
|
||
} | ||
|
||
private void init() { | ||
root = new Pane(); | ||
endXProperty.bind(root.widthProperty().subtract(OFFSET)); | ||
horizontalAxis = new Line(OFFSET, OFFSET * 3, endXProperty.get(), OFFSET * 3); | ||
horizontalAxis.endXProperty().bind(endXProperty); | ||
horizontalAxis.setStyle("-fx-stroke: #B3A9A3; -fx-stroke-width: 2px;"); | ||
root.getChildren().add(horizontalAxis); | ||
distanceBetweenMinutesProperty = horizontalAxis.endXProperty() | ||
.subtract(horizontalAxis.startXProperty()) | ||
.divide(numberOfMinutesProperty); | ||
|
||
} | ||
|
||
public Pane getRoot() { | ||
return root; | ||
} | ||
|
||
private void addAnnotation(Annotation a) { | ||
Platform.runLater(() -> { | ||
var da = new DisplayedAnnotation(a, new Label(), new Line()); | ||
displayedAnno.add(da); | ||
da.addTo(root, horizontalAxis, distanceBetweenMinutesProperty, toolBox.getEventBus()); | ||
}); | ||
} | ||
|
||
private void removeAnnotation(Annotation a) { | ||
Platform.runLater(() -> { | ||
for (int i = 0; i < displayedAnno.size(); i++) { | ||
var d = displayedAnno.get(i); | ||
if (d.annotation().getObservationUuid().equals(a.getObservationUuid())) { | ||
displayedAnno.remove(i); | ||
d.removeFrom(root); | ||
break; | ||
} | ||
} | ||
}); | ||
} | ||
|
||
|
||
|
||
private void setMedia(Media media) { | ||
displayedAnno.forEach(d -> d.removeFrom(root)); | ||
displayedAnno.clear(); | ||
if (media == null || media.getDuration() == null) { | ||
if (timeline != null) { | ||
timeline.removeFrom(root); | ||
} | ||
} | ||
else { | ||
var numberOfMinutes = Math.ceil(media.getDuration().toMillis() / 1000D / 60D); | ||
numberOfMinutesProperty.set(numberOfMinutes); | ||
|
||
var tickMarks = new ArrayList<TickMark>(); | ||
for (int i = 0; i <= numberOfMinutes; i++) { | ||
tickMarks.add(new TickMark(i, new Line(), new Label())); | ||
} | ||
timeline = new Timeline(media, tickMarks); | ||
timeline.addTo(root, horizontalAxis, distanceBetweenMinutesProperty); | ||
} | ||
} | ||
|
||
|
||
} |
Oops, something went wrong.