From 2a9ca3246ad2dbc960f44442389facba3d83cddc Mon Sep 17 00:00:00 2001 From: Adrian Stoica <george.a.stoica@ntnu.no> Date: Fri, 11 Oct 2019 21:14:24 +0200 Subject: [PATCH] added tooltip to marker; this should fix issue #10 --- .../simpleex/ui/AbstractFxAppController.java | 453 ++++++++++-------- 1 file changed, 264 insertions(+), 189 deletions(-) diff --git a/simpleexample2/fxui/src/main/java/simpleex/ui/AbstractFxAppController.java b/simpleexample2/fxui/src/main/java/simpleex/ui/AbstractFxAppController.java index 68b115f..b8f6e97 100644 --- a/simpleexample2/fxui/src/main/java/simpleex/ui/AbstractFxAppController.java +++ b/simpleexample2/fxui/src/main/java/simpleex/ui/AbstractFxAppController.java @@ -1,189 +1,264 @@ -package simpleex.ui; - -import com.fasterxml.jackson.databind.ObjectMapper; -import fxmapcontrol.Location; -import fxmapcontrol.MapBase; -import fxmapcontrol.MapItemsControl; -import fxmapcontrol.MapNode; -import fxmapcontrol.MapProjection; -import javafx.collections.FXCollections; -import javafx.fxml.FXML; -import javafx.geometry.Point2D; -import javafx.scene.Node; -import javafx.scene.control.Alert; -import javafx.scene.control.Alert.AlertType; -import javafx.util.Callback; -import javafx.scene.control.ButtonType; -import javafx.scene.control.ListCell; -import javafx.scene.control.ListView; -import javafx.scene.control.Slider; -import simpleex.core.LatLong; -import simpleex.json.LatLongsModule; - -/* -@startuml -class AbstractFxAppController -class LatLongs -class BorderPane -class "ListView<LatLong>" as ListView -class "fxmapcontrol.MapBase" as MapBase - -BorderPane *--> ListView: "left" -BorderPane *--> MapBase: "center" - -AbstractFxAppController --> MapBase: "mapView" -AbstractFxAppController --> ListView: "locationListView" -@enduml - */ - -/** - * The controller for the app. - * @author hal - * - */ -public abstract class AbstractFxAppController { - - private LatLongsDataAccess dataAccess; - - protected LatLongsDataAccess getDataAccess() { - return dataAccess; - } - - protected void setDataAccess(final LatLongsDataAccess dataAccess) { - this.dataAccess = dataAccess; - if (locationListView != null) { - updateLocationViewList(0); - } - } - - @FXML - private ListView<LatLong> locationListView; - - @FXML - private MapBase mapView; - - private MapItemsControl<MapNode> markersParent; - private MapMarker marker = null; - private DraggableNodeController draggableMapController = null; - private DraggableNodeController draggableMarkerController = null; - - @FXML - private Slider zoomSlider; - - @FXML - private void initialize() { - // map stuff - // mapView.getChildren().add(MapTileLayer.getOpenStreetMapLayer()); - zoomSlider.valueProperty() - .addListener((prop, oldValue, newValue) -> mapView.setZoomLevel(zoomSlider.getValue())); - zoomSlider.setValue(8); - markersParent = new MapItemsControl<MapNode>(); - mapView.getChildren().add(markersParent); - draggableMapController = new DraggableNodeController(this::handleMapDragged); - draggableMapController.setImmediate(true); - draggableMapController.attach(mapView); - draggableMarkerController = new DraggableNodeController(this::handleMarkerDragged); - // the location list - locationListView.getSelectionModel().selectedIndexProperty() - .addListener((prop, oldValue, newValue) -> updateMapMarker(true)); - //connect the cell renderer to the list - locationListView.setCellFactory(listView -> new LatLongCell()); - } - - private void handleMapDragged(final Node node, final double dx, final double dy) { - final MapProjection projection = mapView.getProjection(); - final Point2D point = projection.locationToViewportPoint(mapView.getCenter()); - final Location newCenter = projection.viewportPointToLocation(point.add(-dx, -dy)); - mapView.setCenter(newCenter); - } - - private void handleMarkerDragged(final Node node, final double dx, final double dy) { - final MapProjection projection = mapView.getProjection(); - final Point2D point = projection.locationToViewportPoint(marker.getLocation()); - final Location newLocation = projection.viewportPointToLocation(point.add(dx, dy)); - dataAccess.setLatLong(locationListView.getSelectionModel().getSelectedIndex(), - location2LatLong(newLocation)); - updateLocationViewListSelection(false); - } - - private LatLong location2LatLong(final Location newLocation) { - return new LatLong(newLocation.getLatitude(), newLocation.getLongitude()); - } - - private void updateMapMarker(final boolean centerOnMarker) { - final int num = locationListView.getSelectionModel().getSelectedIndex(); - if (num < 0) { - markersParent.getItems().clear(); - if (draggableMarkerController != null) { - draggableMarkerController.detach(marker); - } - marker = null; - } else { - final LatLong latLong = dataAccess.getLatLong(num); - if (marker == null) { - marker = new MapMarker(latLong); - markersParent.getItems().add(marker); - if (draggableMarkerController != null) { - draggableMarkerController.attach(marker); - } - } else { - marker.setLocation(latLong); - } - if (centerOnMarker) { - mapView.setCenter(marker.getLocation()); - } - } - } - - @FXML - void handleAddLocation() { - final Location center = mapView.getCenter(); - final LatLong latLong = location2LatLong(center); - final int pos = dataAccess.addLatLong(latLong); - updateLocationViewList(pos); - } - - private void updateLocationViewListSelection(final Boolean updateMapMarker) { - final int selectedIndex = locationListView.getSelectionModel().getSelectedIndex(); - locationListView.getItems().set(selectedIndex, dataAccess.getLatLong(selectedIndex)); - if (updateMapMarker != null) { - updateMapMarker(updateMapMarker); - } - } - - protected void updateLocationViewList(int selectedIndex) { - final LatLong[] latLongs = dataAccess.getAllLatLongs().toArray(new LatLong[0]); - final int oldSelectionIndex = locationListView.getSelectionModel().getSelectedIndex(); - locationListView.setItems(FXCollections.observableArrayList(latLongs)); - if (selectedIndex < 0 || selectedIndex >= latLongs.length) { - selectedIndex = oldSelectionIndex; - } - if (selectedIndex >= 0 && selectedIndex < latLongs.length) { - locationListView.getSelectionModel().select(selectedIndex); - } - - } - - private ObjectMapper objectMapper; - - /** - * Gets the ObjectMapper used by this controller. - * @return the ObjectMapper used by this controller - */ - public ObjectMapper getObjectMapper() { - if (objectMapper == null) { - objectMapper = new ObjectMapper(); - objectMapper.registerModule(new LatLongsModule()); - } - return objectMapper; - } - - protected void showExceptionDialog(final String message) { - final Alert alert = new Alert(AlertType.ERROR, message, ButtonType.CLOSE); - alert.showAndWait(); - } - - protected void showExceptionDialog(final String message, final Exception e) { - showExceptionDialog(message + ": " + e.getLocalizedMessage()); - } -} +package simpleex.ui; + +import java.util.Iterator; + +import com.fasterxml.jackson.databind.ObjectMapper; +import fxmapcontrol.Location; +import fxmapcontrol.MapBase; +import fxmapcontrol.MapItemsControl; +import fxmapcontrol.MapNode; +import fxmapcontrol.MapProjection; +import javafx.collections.FXCollections; +import javafx.fxml.FXML; +import javafx.geometry.Insets; +import javafx.geometry.Point2D; +import javafx.scene.Node; +import javafx.scene.control.Alert; +import javafx.scene.control.Alert.AlertType; +import javafx.scene.layout.HBox; +import javafx.util.Callback; +import javafx.scene.control.ButtonType; +import javafx.scene.control.Label; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.control.OverrunStyle; +import javafx.scene.control.Slider; +import javafx.scene.control.Tooltip; +import simpleex.core.LatLong; +import simpleex.core.MetaData; +import simpleex.json.LatLongsModule; + +/* +@startuml +class AbstractFxAppController +class LatLongs +class BorderPane +class "ListView<LatLong>" as ListView +class "fxmapcontrol.MapBase" as MapBase + +BorderPane *--> ListView: "left" +BorderPane *--> MapBase: "center" + +AbstractFxAppController --> MapBase: "mapView" +AbstractFxAppController --> ListView: "locationListView" +@enduml + */ + +/** + * The controller for the app. + * @author hal + * + */ +public abstract class AbstractFxAppController { + + private LatLongsDataAccess dataAccess; + + protected LatLongsDataAccess getDataAccess() { + return dataAccess; + } + + protected void setDataAccess(final LatLongsDataAccess dataAccess) { + this.dataAccess = dataAccess; + if (locationListView != null) { + updateLocationViewList(0); + } + } + + @FXML + private ListView<LatLong> locationListView; + + @FXML + private MapBase mapView; + + private MapItemsControl<MapNode> markersParent; + private MapMarker marker = null; + private DraggableNodeController draggableMapController = null; + private DraggableNodeController draggableMarkerController = null; + private Tooltip markerTooltip = null; + + + @FXML + private Slider zoomSlider; + + @FXML + private void initialize() { + // map stuff + // mapView.getChildren().add(MapTileLayer.getOpenStreetMapLayer()); + zoomSlider.valueProperty() + .addListener((prop, oldValue, newValue) -> mapView.setZoomLevel(zoomSlider.getValue())); + zoomSlider.setValue(8); + markersParent = new MapItemsControl<MapNode>(); + mapView.getChildren().add(markersParent); + draggableMapController = new DraggableNodeController(this::handleMapDragged); + draggableMapController.setImmediate(true); + draggableMapController.attach(mapView); + draggableMarkerController = new DraggableNodeController(this::handleMarkerDragged); + // the location list + locationListView.getSelectionModel().selectedIndexProperty() + .addListener((prop, oldValue, newValue) -> updateMapMarker(true)); + //connect the cell renderer to the list + locationListView.setCellFactory(listView -> new LatLongCell()); + } + + private void handleMapDragged(final Node node, final double dx, final double dy) { + final MapProjection projection = mapView.getProjection(); + final Point2D point = projection.locationToViewportPoint(mapView.getCenter()); + final Location newCenter = projection.viewportPointToLocation(point.add(-dx, -dy)); + mapView.setCenter(newCenter); + } + + private void handleMarkerDragged(final Node node, final double dx, final double dy) { + final MapProjection projection = mapView.getProjection(); + final Point2D point = projection.locationToViewportPoint(marker.getLocation()); + final Location newLocation = projection.viewportPointToLocation(point.add(dx, dy)); + dataAccess.setLatLong(locationListView.getSelectionModel().getSelectedIndex(), + location2LatLong(newLocation)); + updateLocationViewListSelection(false); + } + + private LatLong location2LatLong(final Location newLocation) { + return new LatLong(newLocation.getLatitude(), newLocation.getLongitude()); + } + + private void updateMapMarker(final boolean centerOnMarker) { + final int num = locationListView.getSelectionModel().getSelectedIndex(); + if (num < 0) { + markersParent.getItems().clear(); + if (draggableMarkerController != null) { + draggableMarkerController.detach(marker); + } + marker = null; + } else { + final LatLong latLong = dataAccess.getLatLong(num); + if (marker == null) { + marker = new MapMarker(latLong); + markersParent.getItems().add(marker); + if (draggableMarkerController != null) { + draggableMarkerController.attach(marker); + } + } else { + marker.setLocation(latLong); + } + if (centerOnMarker) { + mapView.setCenter(marker.getLocation()); + } + } + prepareMarkerTooltip(); + } + + void prepareMarkerTooltip() { + if ((marker != null) && (this.markerTooltip != null)) { + Tooltip.uninstall(marker, markerTooltip); + } + + Tooltip tooltip = new Tooltip(); + tooltip.setPrefWidth(300.0); + tooltip.setWrapText(true); + tooltip.setTextOverrun(OverrunStyle.ELLIPSIS); + tooltip.setStyle(" -fx-background: rgba(255, 237, 131);\r\n" + + " -fx-text-fill: black;\r\n" + + " -fx-background-color: rgba(255, 237, 131, 0.8);\r\n" + + " -fx-background-radius: 6px;\r\n" + + " -fx-background-insets: 0;\r\n" + + " -fx-padding: 0.667em 0.75em 0.667em 0.75em; /* 10px */\r\n" + + " -fx-effect: dropshadow( three-pass-box , rgba(0,0,0,0.5) , 10, 0.0 , 0 , 3 );\r\n" + + " -fx-font-size: 0.85em;"); + + final int num = locationListView.getSelectionModel().getSelectedIndex(); + if (num >= 0) { + final LatLong latLong = dataAccess.getLatLong(num); + String tooltipText = ""; + if(latLong.hasMetaData()) { + if(latLong.getMetaData().hasProperty(MetaData.NAME_PROPERTY)) { + tooltipText += latLong.getMetaData().getProperty(MetaData.NAME_PROPERTY) +"\n"; + } + tooltipText += latLong.toString()+"\n"; + if(latLong.getMetaData().hasProperty(MetaData.DESCRIPTION_PROPERTY)) { + tooltipText += latLong.getMetaData().getProperty(MetaData.DESCRIPTION_PROPERTY) +"\n"; + } + MetaData metaData = latLong.getMetaData(); + + final Iterator<String> props = metaData.propertyNames(); + if (props.hasNext()) { + String propString = "\n"; + while (props.hasNext()) { + String propName = props.next(); + if((propName != MetaData.NAME_PROPERTY) + &&(propName != MetaData.DESCRIPTION_PROPERTY)) { + propString += propName + ":" + metaData.getProperty(propName) + "\n"; + } + } + if(!propString.isBlank()) tooltipText += propString + "\n"; + } + + + final Iterator<String> tags = metaData.tags(); + if (tags.hasNext()) { + String tagString = "\n| "; + while (tags.hasNext()) { + tagString += tags.next() + " | "; + } + tooltipText += tagString + "\n"; + } + + } else { + tooltipText += latLong.toString(); + } + tooltip.setText(tooltipText); + } + this.markerTooltip = tooltip; + Tooltip.install(marker, tooltip); + } + + @FXML + void handleAddLocation() { + final Location center = mapView.getCenter(); + final LatLong latLong = location2LatLong(center); + final int pos = dataAccess.addLatLong(latLong); + updateLocationViewList(pos); + } + + private void updateLocationViewListSelection(final Boolean updateMapMarker) { + final int selectedIndex = locationListView.getSelectionModel().getSelectedIndex(); + locationListView.getItems().set(selectedIndex, dataAccess.getLatLong(selectedIndex)); + if (updateMapMarker != null) { + updateMapMarker(updateMapMarker); + } + } + + protected void updateLocationViewList(int selectedIndex) { + final LatLong[] latLongs = dataAccess.getAllLatLongs().toArray(new LatLong[0]); + final int oldSelectionIndex = locationListView.getSelectionModel().getSelectedIndex(); + locationListView.setItems(FXCollections.observableArrayList(latLongs)); + if (selectedIndex < 0 || selectedIndex >= latLongs.length) { + selectedIndex = oldSelectionIndex; + } + if (selectedIndex >= 0 && selectedIndex < latLongs.length) { + locationListView.getSelectionModel().select(selectedIndex); + } + + } + + private ObjectMapper objectMapper; + + /** + * Gets the ObjectMapper used by this controller. + * @return the ObjectMapper used by this controller + */ + public ObjectMapper getObjectMapper() { + if (objectMapper == null) { + objectMapper = new ObjectMapper(); + objectMapper.registerModule(new LatLongsModule()); + } + return objectMapper; + } + + protected void showExceptionDialog(final String message) { + final Alert alert = new Alert(AlertType.ERROR, message, ButtonType.CLOSE); + alert.showAndWait(); + } + + protected void showExceptionDialog(final String message, final Exception e) { + showExceptionDialog(message + ": " + e.getLocalizedMessage()); + } +} -- GitLab