diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/.ratings b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/.ratings index 86b47393dc290e086696323a5f07d0be07adb0bb..0cf1dac697e971d2c67647128a92cd5dc8451b10 100644 --- a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/.ratings +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/.ratings @@ -1 +1 @@ -AF3FXViewPart.java 9e119a36de6ab9e1a07cd416de0126a89514bd38 GREEN +AF3FXViewPart.java 96871186e27ace44069441dbf7ac8ebcd7753390 GREEN diff --git a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/AF3FXViewPart.java b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/AF3FXViewPart.java index 9e119a36de6ab9e1a07cd416de0126a89514bd38..96871186e27ace44069441dbf7ac8ebcd7753390 100644 --- a/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/AF3FXViewPart.java +++ b/org.fortiss.tooling.common.ui/src/org/fortiss/tooling/common/ui/javafx/AF3FXViewPart.java @@ -16,17 +16,21 @@ package org.fortiss.tooling.common.ui.javafx; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; import java.net.URL; import javafx.application.Platform; import javafx.fxml.FXMLLoader; +import javafx.scene.Node; import javafx.scene.Scene; +import javafx.scene.layout.AnchorPane; import javafx.scene.layout.Pane; import org.apache.commons.lang3.SystemUtils; import org.eclipse.fx.ui.workbench3.FXViewPart; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Composite; +import org.eclipse.ui.part.ViewPart; /** * Base class to create eclipse (SWT-based) views using JavaFX GUI elements. This class requires @@ -54,48 +58,49 @@ public abstract class AF3FXViewPart extends FXViewPart { // NO_UCD /** References the stub that defines the FXML file defining the layout and controller. */ private Class<? extends AF3FXViewPart> viewerClass; + /** References the root {@link Pane} to which GUI elements must be added to be displayed. */ + private Pane root; + /** - * Constructor. Allows to pass the view's FXML definition file along with style. + * Constructor that is intended for {@link ViewPart}s which host a single view/node/control. * * @param viewerClass - * Stub class that defines the FXML file defining the layout and controller - * @param fXMLLocation - * location of the fXML file relative to the source (resource) folders. + * Class implementing the {@link AF3FXViewPart} to load resources from its containing + * bundle. + * @param cssLocation + * style sheet file path for the appearance settings relative to the source + * (resource) folders. Pass {@code null} if the OS-native look shall be used. * @throws Exception * if the given FXML file path is not given or if it cannot be found in any of the * source (resource) folders. */ - public AF3FXViewPart(Class<? extends AF3FXViewPart> viewerClass, String fXMLLocation) + public AF3FXViewPart(Class<? extends AF3FXViewPart> viewerClass, String cssLocation) throws Exception { - if(fXMLLocation == null) { - throw new Exception( - "The location of the FXML file defining the gui elements was not given," + - "or it cannot be found at the given location (" + fXMLLocation + ") " + - "The view will NOT be created."); - } - - Platform.setImplicitExit(false); - this.viewerClass = viewerClass; - this.fXMLLocation = fXMLLocation; + // Initializing the base constructor with "null" for the FXML file indicates a + // single-view/node/control usecase. + this(viewerClass, null, cssLocation); } /** * Constructor. Allows to pass the view's FXML definition file. * * @param viewerClass - * Stub class that defines the FXML file defining the layout and controller + * Class implementing the {@link AF3FXViewPart} to load resources from its containing + * bundle. * @param fXMLLocation * location of the fXML file relative to the source (resource) folders. * @param cssLocation * style sheet file path for the appearance settings relative to the source - * (resource) folders. + * (resource) folders. Pass {@code null} if the OS-native look shall be used. * @throws Exception * if the given FXML file path is not given or if it cannot be found in any of the * source (resource) folders. */ public AF3FXViewPart(Class<? extends AF3FXViewPart> viewerClass, String fXMLLocation, String cssLocation) throws Exception { - this(viewerClass, fXMLLocation); + Platform.setImplicitExit(false); + this.viewerClass = viewerClass; + this.fXMLLocation = fXMLLocation; this.cssLocation = cssLocation; } @@ -118,14 +123,28 @@ public abstract class AF3FXViewPart extends FXViewPart { // NO_UCD /** {@inheritDoc} */ @Override protected Scene createFxScene() { - Pane root = null; FXMLLoader loader = new FXMLLoader(); loader.setClassLoader(viewerClass.getClassLoader()); - loader.setLocation(viewerClass.getResource(fXMLLocation)); - try { - root = (Pane)loader.load(); - } catch(IOException e) { - throw new RuntimeException("Cannot load the resource located at " + fXMLLocation, e); + if(fXMLLocation != null) { + loader.setLocation(viewerClass.getResource(fXMLLocation)); + try { + root = (Pane)loader.load(); + } catch(IOException e) { + throw new RuntimeException("Cannot load the resource located at " + fXMLLocation, e); + } + } else { + root = new AnchorPane(); + try { + viewerClass.getMethod("initialize").invoke(this); + } catch(IllegalAccessException | IllegalArgumentException | InvocationTargetException | + SecurityException e) { + throw new RuntimeException("The initialize method of the View " + + viewerClass.getSimpleName() + " threw an exception!", e); + } catch(NoSuchMethodException e) { + throw new RuntimeException("For single Controller views, an initialize method is " + + "required that creates the control! This cannot be done in constructors " + + "since the JavaFX toolkit is not initialized in that stage.", e); + } } if(cssLocation != null) { @@ -134,6 +153,7 @@ public abstract class AF3FXViewPart extends FXViewPart { // NO_UCD if(!isCustomStyleSheetApplied) { setNativeLook(root); } + Scene scene = new Scene(root); return scene; } @@ -145,9 +165,50 @@ public abstract class AF3FXViewPart extends FXViewPart { // NO_UCD } /** - * Sets the native look of the target platform to break the UI. Most of the UI code is based on - * SWT which uses a platform native look. Currently, Windows 8&10, Windows 7, and MacOS X native - * looks are supported. + * Init method that is called after the JavaFX toolkit has been launched. The initialize method + * is needed for single-node/view/control {@link ViewPart}s and for controls in a FXML-based + * view that require an initial configuration. If no initial configuration is required place a + * stub. + * <p> + * For FXML, the initialize method is called via the JavaFX framework. Otherwise, it is called + * via reflection from this class. + */ + protected abstract void initialize(); + + /** + * Adds a single {@link Node} to this view. It embeds the given {@link Node} to the + * {@link AnchorPane} of this view and stretches it to the borders of the view. + * <p> + * NOTE: This method may only be used for single-control views, not FXML-based ones. + * + * @param fxNode + * {@link Node} to be added to the view. + */ + protected void setChildNodeFinal(Node fxNode) { + if(fXMLLocation != null) { + throw new RuntimeException("This method is intended for single-control views. It may" + + " not be used for FXML-based views."); + } + if(!root.getChildren().isEmpty()) { + throw new RuntimeException("The method setChildNodeFinal was invoked more than once." + + " This method is intended for single-control views. If more controls" + + "shall be added, please define an FXML layout file and call the appropriate" + + " constructors."); + } + + root.getChildren().add(fxNode); + // Stretch the node to the borders of the view. In the createScene method, we a create a + // root pane that is an AnchorPane, so we can safely use these calls here. + AnchorPane.setTopAnchor(fxNode, 0.0); + AnchorPane.setRightAnchor(fxNode, 0.0); + AnchorPane.setBottomAnchor(fxNode, 0.0); + AnchorPane.setLeftAnchor(fxNode, 0.0); + } + + /** + * Sets the native look of the target platform to not break the UI experience. Most of the UI + * code is based on SWT which uses a platform native look. Currently, Windows 8&10, Windows 7, + * and MacOS X native looks are supported. */ private void setNativeLook(Pane pane) { if(SystemUtils.IS_OS_WINDOWS_10 || SystemUtils.IS_OS_WINDOWS_8) {