/* * Copyright 2012-2013 Canoo Engineering AG. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.opendolphin.demo.push; import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline; import javafx.application.Application; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; import javafx.event.Event; import javafx.event.EventHandler; import javafx.event.EventType; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import javafx.scene.control.TextField; import javafx.scene.effect.*; import javafx.scene.input.MouseEvent; import javafx.scene.layout.*; import javafx.scene.paint.Color; import javafx.scene.paint.LinearGradientBuilder; import javafx.scene.paint.Paint; import javafx.scene.paint.Stop; import javafx.scene.shape.Rectangle; import javafx.scene.shape.RectangleBuilder; import javafx.scene.shape.StrokeType; import javafx.stage.Stage; import javafx.util.Duration; import org.opendolphin.binding.Converter; import org.opendolphin.binding.JFXBinder; import org.opendolphin.binding.JavaFxUtil; import org.opendolphin.core.Attribute; import org.opendolphin.core.client.ClientAttribute; import org.opendolphin.core.client.ClientDolphin; import org.opendolphin.core.client.ClientPresentationModel; import org.opendolphin.core.client.comm.OnFinishedHandlerAdapter; import org.opendolphin.demo.FX; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.HashMap; import java.util.List; import java.util.Map; import static org.opendolphin.demo.DemoStyle.defaultStyle; public class PushView extends Application { private static ClientDolphin dolphin; public static void setClientDolphin(ClientDolphin clientDolphin) { dolphin = clientDolphin; } private void longPoll() { dolphin.send(VehicleConstants.CMD_UPDATE, new OnFinishedHandlerAdapter() { @Override public void onFinished(List<ClientPresentationModel> presentationModels) { longPoll(); } }); } @Override public void start(Stage stage) throws Exception { final ClientPresentationModel selectedVehicle = dolphin.presentationModel(VehicleConstants.ID_SELECTED, VehicleConstants.ALL_ATTRIBUTES); final ObservableList<ClientPresentationModel> observableListOfPms = FXCollections.observableArrayList(); final Map<String, Rectangle> pmIdsToRect = new HashMap<String, Rectangle>(); // pmId to rectangle Rectangle selRect = newVehicle(null); final TextField selX = new TextField(); final TextField selY = new TextField(); Rectangle selAngle = new Rectangle(); selAngle.setWidth(26); selAngle.setHeight(5); selAngle.setFill(LinearGradientBuilder.create().stops(new Stop(0.6, Color.WHITE), new Stop(1, Color.RED)).build()); HBox header = new HBox(); header.setAlignment(Pos.CENTER); header.setPrefWidth(700); header.setPrefHeight(40); header.setSpacing(10); header.getChildren().add(new Label("Selected")); header.getChildren().add(selRect); header.getChildren().add(new Label("X")); header.getChildren().add(selX); header.getChildren().add(new Label("Y")); header.getChildren().add(selY); header.getChildren().add(new Label("Angle")); header.getChildren().add(selAngle); TableColumn columnColor = new TableColumn("Color"); columnColor.setPrefWidth(50); TableColumn columnX = new TableColumn("X"); columnX.setPrefWidth(40); TableColumn columnY = new TableColumn("Y"); columnY.setPrefWidth(40); TableColumn columnA = new TableColumn("Angle"); columnA.setPrefWidth(40); final TableView table = new TableView(); // table.setOpacity(0.2); table.getColumns().add(columnColor); table.getColumns().add(columnX); table.getColumns().add(columnY); table.getColumns().add(columnA); table.setItems(observableListOfPms); JavaFxUtil.value(VehicleConstants.ATT_COLOR, columnColor); JavaFxUtil.value(VehicleConstants.ATT_X, columnX); JavaFxUtil.value(VehicleConstants.ATT_Y, columnY); JavaFxUtil.value(VehicleConstants.ATT_ROTATE, columnA); // // TODO: Logo can currently only added using GroovyFX // DolphinLogo logo = new DolphinLogo(); // logo.setWidth(401); // logo.setHeight(257); // logo.opacity = 0.1d final Group parent = new Group(); parent.setEffect(createDropShadow()); parent.getChildren().add(RectangleBuilder.create().width(400).height(400).fill(Color.TRANSPARENT).build()); // rigid area BorderPane borderPane = new BorderPane(); borderPane.setPadding(new Insets(20, 20, 20, 20)); borderPane.setTop(header); borderPane.setLeft(table); borderPane.setCenter(parent); // when a new pm is added to the list, create the rectangles along with their animations observableListOfPms.addListener(new ListChangeListener<ClientPresentationModel>() { @Override public void onChanged(Change<? extends ClientPresentationModel> listChange) { while (listChange.next()) { /*sigh*/ for (final ClientPresentationModel pm : listChange.getAddedSubList()) { final Rectangle rectangle = newVehicle(pm.getId()); rectangle.setOnMouseClicked(new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent mouseEvent) { dolphin.apply(pm).to(selectedVehicle); } }); pmIdsToRect.put(pm.getId(), rectangle); for (Attribute attribute : pm.getAttributes()) { final String prop = attribute.getPropertyName(); if (prop.equals(VehicleConstants.ATT_COLOR)) break; // only for the moment - until we convert types rectangle.getProperties().put(prop, pm.getAt(prop).getValue()); pm.getAt(prop).addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName().equals("value")) { Timeline timeline = new Timeline(); ClientAttribute attr = (ClientAttribute) evt.getSource(); Double newValue = Double.valueOf(evt.getNewValue().toString()); if (attr.getPropertyName().equals(VehicleConstants.ATT_X)) { KeyValue xKeyValue = new KeyValue(rectangle.xProperty(), newValue); timeline.getKeyFrames().add(new KeyFrame(Duration.seconds(0.5), xKeyValue)); } if (attr.getPropertyName().equals(VehicleConstants.ATT_Y)) { KeyValue yKeyValue = new KeyValue(rectangle.yProperty(), newValue); timeline.getKeyFrames().add(new KeyFrame(Duration.seconds(0.5), yKeyValue)); } if (attr.getPropertyName().equals(VehicleConstants.ATT_ROTATE)) { KeyValue rotateKeyValue = new KeyValue(rectangle.rotateProperty(), newValue); timeline.getKeyFrames().add(new KeyFrame(Duration.seconds(0.5), rotateKeyValue)); } timeline.play(); // sgb.timeline { // at(0.5.s) { change(rectangle, prop) to evt.newValue tween "ease_both" } // }.play() } } }); } parent.getChildren().add(rectangle); } } } }); // startup and main loop dolphin.send(VehicleConstants.CMD_PULL, new OnFinishedHandlerAdapter() { @Override public void onFinished(List<ClientPresentationModel> presentationModels) { for (ClientPresentationModel pm : presentationModels) { observableListOfPms.add(pm); } // fadeTransition(1.s, node:table, to:1).playFromStart() longPoll(); } }); // all the bindings ... JFXBinder.bind(VehicleConstants.ATT_X).of(selectedVehicle).to(FX.TEXT).of(selX); // simple binding + action selX.addEventHandler(EventType.ROOT, new EventHandler<Event>() { @Override public void handle(Event event) { selectedVehicle.getAt(VehicleConstants.ATT_X).setValue(new StringToIntegerConverter().convert(selX.getText())); } }); JFXBinder.bind(VehicleConstants.ATT_Y).of(selectedVehicle).to(FX.TEXT).of(selY); // example of a "bidirectional" binding JFXBinder.bind(FX.TEXT).of(selY).to(VehicleConstants.ATT_Y).of(selectedVehicle, new StringToIntegerConverter()); JFXBinder.bind(VehicleConstants.ATT_COLOR).of(selectedVehicle).to(FX.FILL).of(selRect, new StringToColorConverter()); JFXBinder.bind(VehicleConstants.ATT_ROTATE).of(selectedVehicle).to(FX.ROTATE).of(selAngle, new StringToDoubleConverter()); // bind 'selectedItem' of table.selectionModel to { ... } table.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() { @Override public void changed(ObservableValue observableValue, Object oldValue, Object newValue) { ClientPresentationModel selectedPm = (ClientPresentationModel) newValue; dolphin.apply(selectedPm).to(selectedVehicle); } }); // bind COLOR of selectedVehicle to { ... } selectedVehicle.getAt(VehicleConstants.ATT_COLOR).addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { Object from = evt.getOldValue(); Object to = evt.getNewValue(); if (from != null) { pmIdsToRect.get(from.toString()).setStrokeWidth(0); } if (to != null) { pmIdsToRect.get(to.toString()).setStrokeWidth(3); } } }); selectedVehicle.getAt(VehicleConstants.ATT_COLOR).addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { Object to = evt.getNewValue(); if (to != null) { table.getSelectionModel().select(dolphin.findPresentationModelById(to.toString())); } } }); Scene scene = new Scene(borderPane, 700, 500); defaultStyle(scene); stage.setScene(scene); stage.setTitle("Dolphin push demo"); stage.show(); } private DropShadow createDropShadow() { Light.Distant light = new Light.Distant(); light.setAzimuth(-135.0); Lighting lighting = new Lighting(); lighting.setLight(light); return DropShadowBuilder.create().offsetY(2).offsetX(2).radius(3).input(lighting).build(); } private Rectangle newVehicle(String id) { Rectangle selRect = new Rectangle(); selRect.setFill(id == null ? null : Paint.valueOf(id)); selRect.setArcWidth(10); selRect.setArcHeight(10); selRect.setWidth(74); selRect.setHeight(20); selRect.setStroke(Paint.valueOf("cyan")); selRect.setStrokeWidth(2); selRect.setStrokeType(StrokeType.OUTSIDE); selRect.setEffect(DropShadowBuilder.create().offsetY(2).offsetX(2).radius(3).build()); // input: lighting{distant(azimuth: -135.0)}) return selRect; } private class StringToColorConverter implements Converter { @Override public Object convert(Object value) { return (value == null) ? Color.TRANSPARENT : Paint.valueOf(value.toString()); } } private class StringToIntegerConverter implements Converter { @Override public Object convert(Object value) { return (value == null) ? 0 : Integer.valueOf(value.toString()); } } private class StringToDoubleConverter implements Converter { @Override public Object convert(Object value) { return (value == null) ? 0 : Double.valueOf(value.toString()); } } }