us.ihmc.scs2.sessionVisualizer.jfx.MainWindowController Maven / Gradle / Ivy
package us.ihmc.scs2.sessionVisualizer.jfx;
import com.jfoenix.controls.JFXDrawer;
import com.jfoenix.controls.JFXDrawer.DrawerDirection;
import com.jfoenix.controls.JFXHamburger;
import com.jfoenix.controls.events.JFXDrawerEvent;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.SubScene;
import javafx.scene.control.Label;
import javafx.scene.control.SplitPane;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import org.apache.commons.lang3.mutable.MutableBoolean;
import us.ihmc.commons.Conversions;
import us.ihmc.messager.javafx.JavaFXMessager;
import us.ihmc.scs2.sessionVisualizer.jfx.HamburgerAnimationTransition.FrameType;
import us.ihmc.scs2.sessionVisualizer.jfx.controllers.SessionAdvancedControlsController;
import us.ihmc.scs2.sessionVisualizer.jfx.controllers.SessionSimpleControlsController;
import us.ihmc.scs2.sessionVisualizer.jfx.controllers.VisualizerController;
import us.ihmc.scs2.sessionVisualizer.jfx.controllers.chart.YoChartGroupPanelController;
import us.ihmc.scs2.sessionVisualizer.jfx.controllers.menu.MainWindowMenuBarController;
import us.ihmc.scs2.sessionVisualizer.jfx.managers.ReferenceFrameManager;
import us.ihmc.scs2.sessionVisualizer.jfx.managers.SessionVisualizerToolkit;
import us.ihmc.scs2.sessionVisualizer.jfx.managers.SessionVisualizerWindowToolkit;
import us.ihmc.scs2.sessionVisualizer.jfx.plotter.Plotter2D;
import us.ihmc.scs2.sessionVisualizer.jfx.tools.CompositePropertyTools;
import us.ihmc.scs2.sessionVisualizer.jfx.tools.JavaFXMissingTools;
import us.ihmc.scs2.sessionVisualizer.jfx.tools.ObservedAnimationTimer;
import us.ihmc.scs2.sessionVisualizer.jfx.tools.YoVariableDatabase;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
public class MainWindowController extends ObservedAnimationTimer implements VisualizerController
{
@FXML
private AnchorPane rootPane;
@FXML
private SplitPane mainGUIPane;
@FXML
private AnchorPane sceneAnchorPane;
@FXML
private SplitPane mainViewSplitPane;
@FXML
private JFXHamburger leftDrawerBurger, rightDrawerBurger;
@FXML
private Label fpsLabel;
@FXML
private MainWindowMenuBarController mainWindowMenuBarController;
@FXML
private SessionSimpleControlsController sessionSimpleControlsController;
@FXML
private SessionAdvancedControlsController sessionAdvancedControlsController;
@FXML
private YoChartGroupPanelController yoChartGroupPanelController;
/**
* The drawer used to hold onto the tools for searching yoVariables.
*/
private final JFXDrawer leftDrawer = new JFXDrawer();
/**
* The drawer used to hold onto custom GUI controls.
*/
private final JFXDrawer rightDrawer = new JFXDrawer();
private final BooleanProperty leftDrawerOpen = new SimpleBooleanProperty(this, "leftDrawerOpenProperty", false);
private final BooleanProperty rightDrawerOpen = new SimpleBooleanProperty(this, "rightDrawerOpenProperty", false);
private final BooleanProperty disableUserControls = new SimpleBooleanProperty(this, "disableUserControlsProperty", false);
private final Property yoNameDisplayProperty = new SimpleObjectProperty<>(this, "yoNameDisplayProperty", YoNameDisplay.UNIQUE_SHORT_NAME);
/**
* Controller for the left pane where variable search and entries are displayed.
*/
private SidePaneController sidePaneController;
/**
* Controller for the right pane where custom user controls are displayed.
*/
private UserSidePaneController userSidePaneController;
private final Plotter2D plotter2D = new Plotter2D();
private SessionVisualizerToolkit globalToolkit;
private SessionVisualizerWindowToolkit windowToolkit;
private SessionVisualizerTopics topics;
private JavaFXMessager messager;
@Override
public void initialize(SessionVisualizerWindowToolkit toolkit)
{
windowToolkit = toolkit;
globalToolkit = toolkit.getGlobalToolkit();
topics = toolkit.getTopics();
messager = toolkit.getMessager();
messager.addFXTopicListener(topics.getDisableUserControls(), m -> disableUserControls.set(m));
messager.bindBidirectional(topics.getYoVariableNameDisplay(), yoNameDisplayProperty, true);
mainWindowMenuBarController.initialize(windowToolkit);
sessionSimpleControlsController.initialize(windowToolkit);
sessionAdvancedControlsController.initialize(windowToolkit);
// Show the advanced controls by default
sessionSimpleControlsController.show(false);
sessionAdvancedControlsController.showProperty().set(true);
yoChartGroupPanelController.initialize(windowToolkit);
rootPane.getChildren().set(1, leftDrawer);
JavaFXMissingTools.setAnchorConstraints(leftDrawer, 25, 0, 0, 0);
leftDrawer.getChildren().add(rightDrawer);
leftDrawer.setDefaultDrawerSize(300);
leftDrawer.setDirection(DrawerDirection.LEFT);
rightDrawer.getChildren().add(mainGUIPane);
rightDrawer.setDirection(DrawerDirection.RIGHT);
rightDrawer.setDefaultDrawerSize(300);
try
{
FXMLLoader loader = new FXMLLoader(SessionVisualizerIOTools.SIDE_PANE_URL);
setupLeftDrawer(loader.load());
sidePaneController = loader.getController();
sidePaneController.initialize(toolkit.getGlobalToolkit());
}
catch (IOException e)
{
e.printStackTrace();
}
try
{
FXMLLoader loader = new FXMLLoader(SessionVisualizerIOTools.USER_SIDE_PANE_URL);
setupRightDrawer(loader.load());
userSidePaneController = loader.getController();
userSidePaneController.initialize(toolkit);
userSidePaneController.computedPrefWidthProperty().addListener((o, oldValue, newValue) -> rightDrawer.setDefaultDrawerSize(newValue.doubleValue()));
}
catch (IOException e)
{
e.printStackTrace();
}
setupViewport3D(toolkit.getGlobalToolkit().getViewport3DManager().getPane());
setupPlotter2D(plotter2D);
messager.addFXTopicListener(topics.getPlotter2DTrackCoordinateRequest(), m ->
{
YoVariableDatabase rootRegistryDatabase = toolkit.getYoManager().getRootRegistryDatabase();
ReferenceFrameManager referenceFrameManager = toolkit.getReferenceFrameManager();
plotter2D.coordinateToTrackProperty().setValue(CompositePropertyTools.toTuple2DProperty(rootRegistryDatabase, referenceFrameManager, m));
});
messager.addFXTopicListener(topics.getYoChartGroupLoadConfiguration(),
m -> yoChartGroupPanelController.loadChartGroupConfiguration(m.getKey(), m.getValue()));
messager.addFXTopicListener(topics.getYoChartGroupSaveConfiguration(),
m -> yoChartGroupPanelController.saveChartGroupConfiguration(m.getKey(), m.getValue()));
}
public void setupViewport3D(Pane viewportPane)
{
sceneAnchorPane.getChildren().set(0, viewportPane);
AnchorPane.setTopAnchor(viewportPane, 0.0);
AnchorPane.setRightAnchor(viewportPane, 0.0);
AnchorPane.setBottomAnchor(viewportPane, 0.0);
AnchorPane.setLeftAnchor(viewportPane, 0.0);
globalToolkit.getSnapshotManager().registerRecordable(viewportPane);
}
private Property showOverheadPlotterProperty;
public Property setupPlotter2D(Plotter2D plotter2D)
{
SubScene plotter2DScene = new SubScene(plotter2D, 100, 10);
Pane pane = new Pane(plotter2DScene);
plotter2DScene.heightProperty().bind(pane.heightProperty());
plotter2DScene.widthProperty().bind(pane.widthProperty());
plotter2D.getRoot().getChildren().add(globalToolkit.getYoGraphicFXManager().getRootNode2D());
showOverheadPlotterProperty = messager.createPropertyInput(topics.getShowOverheadPlotter(), false);
showOverheadPlotterProperty.addListener((o, oldValue, newValue) ->
{
if (newValue)
{
if (!mainViewSplitPane.getItems().contains(pane))
{
mainViewSplitPane.getItems().add(pane);
plotter2D.setScale(400.0 / 5.0);
}
}
else
{
mainViewSplitPane.getItems().remove(pane);
}
});
return showOverheadPlotterProperty;
}
public void setupLeftDrawer(Pane sidePane)
{
configureDrawer(sidePane, leftDrawer, leftDrawerBurger, leftDrawerOpen);
HamburgerAnimationTransition transition = new HamburgerAnimationTransition(leftDrawerBurger, FrameType.BURGER, FrameType.LEFT_CLOSE);
leftDrawer.addEventHandler(Event.ANY, e ->
{
if (e.getTarget() != leftDrawer)
return;
if (e.getEventType() == JFXDrawerEvent.OPENED)
{
sidePaneController.getYoSearchTabPaneController().requestFocusForActiveSearchBox();
}
else if (e.getEventType() == JFXDrawerEvent.CLOSING)
{
transition.setRate(-1);
transition.play();
}
else if (e.getEventType() == JFXDrawerEvent.OPENING)
{
transition.setRate(1);
transition.play();
}
});
}
public void setupRightDrawer(Pane sidePane)
{
configureDrawer(sidePane, rightDrawer, rightDrawerBurger, rightDrawerOpen);
HamburgerAnimationTransition transition = new HamburgerAnimationTransition(rightDrawerBurger, FrameType.LEFT_ANGLE, FrameType.RIGHT_CLOSE);
rightDrawer.addEventHandler(Event.ANY, e ->
{
if (e.getTarget() != rightDrawer)
return;
if (e.getEventType() == JFXDrawerEvent.OPENED)
{
// TODO
}
else if (e.getEventType() == JFXDrawerEvent.CLOSING)
{
transition.setRate(-1);
transition.play();
}
else if (e.getEventType() == JFXDrawerEvent.OPENING)
{
transition.setRate(1);
transition.play();
}
});
}
public void configureDrawer(Pane sidePane, JFXDrawer drawer, Node openCloseControl, BooleanProperty drawerOpenProperty)
{
Pane drawerSidePane = addEdgeToSidePane(sidePane, drawer.getDirection());
drawer.setSidePane(drawerSidePane);
Node remove = drawer.getChildren().remove(drawer.getChildren().size() - 1);
rootPane.addEventHandler(KeyEvent.KEY_PRESSED, (EventHandler super KeyEvent>) e ->
{
if (e.isConsumed())
return;
if (e.getCode() == KeyCode.ESCAPE && drawer.isOpened())
{
drawer.close();
e.consume();
}
});
drawer.setResizeContent(true);
drawer.setResizableOnDrag(true);
drawer.setOverLayVisible(false);
drawer.setContent(remove);
BooleanProperty drawerFullyClosed = new SimpleBooleanProperty(drawer, "drawerFullyClosedProperty", !drawerOpenProperty.get());
drawerOpenProperty.set(drawer.isOpened());
MutableBoolean ignoreOpenChange = new MutableBoolean(false);
drawerOpenProperty.addListener((o, oldValue, newValue) ->
{
if (ignoreOpenChange.booleanValue())
return;
if (newValue && (drawer.isClosing() || drawer.isClosed()))
drawer.open();
else if (drawer.isOpening() || drawer.isOpened())
drawer.close();
});
drawer.addEventHandler(Event.ANY, e ->
{
if (e.getTarget() != drawer)
return;
if (e.getEventType() == JFXDrawerEvent.CLOSED)
{
ignoreOpenChange.setTrue();
drawerOpenProperty.set(false);
ignoreOpenChange.setFalse();
drawerFullyClosed.set(true);
}
if (e.getEventType() == JFXDrawerEvent.OPENING || e.getEventType() == JFXDrawerEvent.OPENED)
{
ignoreOpenChange.setTrue();
drawerOpenProperty.set(true);
ignoreOpenChange.setFalse();
drawerFullyClosed.set(false);
}
});
openCloseControl.setOnMouseClicked(e ->
{
if (drawer.isClosed() || drawer.isClosing())
drawer.open();
else
drawer.close();
});
// By disabling the side pane, we unlink YoVariables (in search tabs) reducing the cost of a run tick for the Session
drawerSidePane.visibleProperty().bind(drawerFullyClosed.not());
drawerSidePane.disableProperty().bind(drawerFullyClosed.or(disableUserControls));
}
private Pane addEdgeToSidePane(Pane sidePane, DrawerDirection contentSide)
{
if (contentSide == DrawerDirection.LEFT || contentSide == DrawerDirection.RIGHT)
{
// Workaround for the drawer resizing:
// Here we make an edge similar to a SplitPane separator on which the cursor will change
StackPane edge = new StackPane();
edge.setStyle("-fx-background-color:white;-fx-effect:innershadow(three-pass-box, #A9A9A9, 2.5, 0.0, -1.0, 0.0)");
edge.setPrefWidth(5.0);
edge.setMinWidth(5.0);
edge.setCursor(Cursor.E_RESIZE);
HBox drawerSidePane;
if (contentSide == DrawerDirection.LEFT)
drawerSidePane = new HBox(sidePane, edge);
else
drawerSidePane = new HBox(edge, sidePane);
// The edge passes on the MouseEvents used for resizing the drawer to the inner StackPane.
edge.addEventHandler(MouseEvent.ANY, event ->
{
if (drawerSidePane.getParent() != null)
drawerSidePane.getParent().fireEvent(event);
});
HBox.setHgrow(sidePane, Priority.ALWAYS);
HBox.setHgrow(edge, Priority.NEVER);
return drawerSidePane;
}
throw new UnsupportedOperationException("Not implemented for: " + contentSide);
}
private long timeLast = -1;
private long timeIntervalBetweenUpdates = TimeUnit.MILLISECONDS.toNanos(500);
private int frameCounter = 0;
private double goodFPSLowerThreshold = 40.0;
private double mediumFPSLowerThreshold = 20.0;
private Color goodFPSColor = Color.FORESTGREEN;
private Color mediumFPSColor = Color.DARKORANGE;
private Color poorFPSColor = Color.RED;
@Override
public void handleImpl(long timeNow)
{
if (timeLast == -1)
{
timeLast = timeNow;
frameCounter = 0;
return;
}
frameCounter++;
if (timeNow - timeLast < timeIntervalBetweenUpdates)
return;
double framesPerSecond = frameCounter / Conversions.nanosecondsToSeconds(timeNow - timeLast);
fpsLabel.setText(String.format("%6.2f FPS", framesPerSecond));
if (framesPerSecond >= goodFPSLowerThreshold)
fpsLabel.setTextFill(goodFPSColor);
else if (framesPerSecond >= mediumFPSLowerThreshold)
fpsLabel.setTextFill(mediumFPSColor);
else
fpsLabel.setTextFill(poorFPSColor);
timeLast = timeNow;
frameCounter = 0;
}
@Override
public void start()
{
super.start();
windowToolkit.start();
}
@Override
public void stop()
{
stopSession();
super.stop();
windowToolkit.stop();
}
public void startSession()
{
yoChartGroupPanelController.start();
sidePaneController.start();
}
public void stopSession()
{
yoChartGroupPanelController.stopAndClear();
sidePaneController.stop();
plotter2D.coordinateToTrackProperty().setValue(null);
}
public AnchorPane getMainPane()
{
return rootPane;
}
public AnchorPane getSceneAnchorPane()
{
return sceneAnchorPane;
}
public SplitPane getMainViewSplitPane()
{
return mainViewSplitPane;
}
public MainWindowMenuBarController getMainWindowMenuBarController()
{
return mainWindowMenuBarController;
}
public SessionSimpleControlsController getSessionSimpleControlsController()
{
return sessionSimpleControlsController;
}
public SessionAdvancedControlsController getSessionAdvancedControlsController()
{
return sessionAdvancedControlsController;
}
public YoChartGroupPanelController getYoChartGroupPanelController()
{
return yoChartGroupPanelController;
}
public SidePaneController getSidePaneController()
{
return sidePaneController;
}
public UserSidePaneController getUserSidePaneController()
{
return userSidePaneController;
}
public BooleanProperty leftSidePaneOpenProperty()
{
return leftDrawerOpen;
}
public BooleanProperty rightSidePaneOpenProperty()
{
return rightDrawerOpen;
}
public Property showOverheadPlotterProperty()
{
return showOverheadPlotterProperty;
}
public BooleanProperty showAdvancedControlsProperty()
{
return sessionAdvancedControlsController.showProperty();
}
public Property yoNameDisplayProperty()
{
return yoNameDisplayProperty;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy