Skip to content
Snippets Groups Projects
Commit a890967c authored by Tiziano Munaro's avatar Tiziano Munaro
Browse files

[UI] Refactor JavaFX composites

* `AF3FXViewParts` no longer implement the `ICompositeFXContainer`
interface. Instead they are reduced to a minimum, referencing a
(potentially hierarchic) `ICompositeFXContainer` with all the layout and
logic.
* FXML layouts are no longer loaded in the constructor of the
`CompositeFXControllerBase`, but in its `initialize` method (executed in
`getOrLoadLayout`).

Issue-Ref: 3437
Issue-Url: https://af3-developer.fortiss.org/issues/3437



Signed-off-by: default avatarTiziano Munaro <munaro@fortiss.org>
parent 07bb0147
No related branches found
No related tags found
1 merge request!823437
This commit is part of merge request !82. Comments created here will be created in the context of that merge request.
AF3FXViewPart.java 67cc6f943239c695bf2965502ccc05091f3dd346 GREEN AF3FXViewPart.java 66e5e5d93b3e03cc2202c9213ee2636d40fb6833 YELLOW
...@@ -15,108 +15,78 @@ ...@@ -15,108 +15,78 @@
+--------------------------------------------------------------------------*/ +--------------------------------------------------------------------------*/
package org.fortiss.tooling.common.ui.javafx; package org.fortiss.tooling.common.ui.javafx;
import static org.fortiss.tooling.common.ui.javafx.util.JavaFXUtils.loadFXML; import static javafx.scene.layout.AnchorPane.setBottomAnchor;
import static javafx.scene.layout.AnchorPane.setLeftAnchor;
import static javafx.scene.layout.AnchorPane.setRightAnchor;
import static javafx.scene.layout.AnchorPane.setTopAnchor;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URL; import java.net.URL;
import java.util.ResourceBundle;
import org.apache.commons.lang3.SystemUtils; import org.apache.commons.lang3.SystemUtils;
import org.eclipse.fx.ui.workbench3.FXViewPart; import org.eclipse.fx.ui.workbench3.FXViewPart;
import org.fortiss.tooling.common.ui.javafx.layout.ICompositeFXController; import org.fortiss.tooling.common.ui.javafx.layout.ICompositeFXController;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.fxml.Initializable;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane; import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane; import javafx.scene.layout.Pane;
/** /**
* Base class to create eclipse (SWT-based) views using JavaFX GUI elements. This class requires * Base class to create eclipse (SWT-based) views using JavaFX GUI elements.
* FXML file that defines the GUI elements of the view.
* *
* @author diewald * @author munaro
*/ */
public abstract class AF3FXViewPart extends FXViewPart public abstract class AF3FXViewPart extends FXViewPart {
implements ICompositeFXController, Initializable {
/** For later use, if we need to define different styles. */ /** For later use, if we need to define different styles. */
private String cssLocation; private String cssLocation;
/** Flag that indicates if a custom style sheet has been applied. */ /** Flag that indicates if a custom style sheet has been applied. */
private boolean isCustomStyleSheetApplied; private boolean isCustomStyleSheetApplied;
/** 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. */ /** References the root {@link Pane} to which GUI elements must be added to be displayed. */
private Pane root; private Pane root;
/** {@link ICompositeFXController}s to be added to a container node within the view part. */ /** Specifies the view part's layout and logic. */
private ICompositeFXController[] containments; private ICompositeFXController controller;
/** /**
* Constructor. * Constructor. Allows to pass the view's FXML definition file.
* *
* @param viewerClass * @param controller
* Class implementing the {@link AF3FXViewPart} to load resources from its containing * {@link ICompositeFXController} specifying the layout of the view part as well as
* bundle. * its logic.
* @param cssLocation * @param cssLocation
* style sheet file path for the appearance settings relative to the source * 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. * (resource) folders. Pass {@code null} if the OS-native look shall be used.
* @param containments
* Optional {@link ICompositeFXController}s to be added to a container node within
* the view
* part.
* @throws Exception * @throws Exception
* if the given FXML file path is not given or if it cannot be found in any of the * if the given FXML file path is not given or if it cannot be found in any of the
* source (resource) folders. * source (resource) folders.
*/ */
public AF3FXViewPart(Class<? extends AF3FXViewPart> viewerClass, String cssLocation, public AF3FXViewPart(ICompositeFXController controller, String cssLocation) throws Exception {
ICompositeFXController... containments) throws Exception {
Platform.setImplicitExit(false); Platform.setImplicitExit(false);
this.viewerClass = viewerClass;
this.cssLocation = cssLocation; this.cssLocation = cssLocation;
this.containments = containments; this.controller = controller;
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
protected Scene createFxScene() { protected Scene createFxScene() {
if(getFXMLLocation() != null) { Node layout = controller.getOrLoadLayout();
URL fxmlResource = viewerClass.getResource(getFXMLLocation());
try { setTopAnchor(layout, 0.0);
root = loadFXML(fxmlResource, this); setRightAnchor(layout, 0.0);
} catch(IOException e) { setBottomAnchor(layout, 0.0);
throw new RuntimeException( setLeftAnchor(layout, 0.0);
"Cannot load the resource located at " + getFXMLLocation(), e);
}
} else {
root = new AnchorPane();
try {
viewerClass.getMethod("initialize", URL.class, ResourceBundle.class).invoke(this,
null, null);
} 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);
}
}
root = new AnchorPane(layout);
if(cssLocation != null) { if(cssLocation != null) {
isCustomStyleSheetApplied = applyStyleSheet(root, getClass(), cssLocation); isCustomStyleSheetApplied = applyStyleSheet(root, getClass(), cssLocation);
} }
if(!isCustomStyleSheetApplied) { if(!isCustomStyleSheetApplied) {
setNativeLook(root); setNativeLook(root);
} }
addContainments(containments);
Scene scene = new Scene(root); Scene scene = new Scene(root);
return scene; return scene;
} }
...@@ -127,36 +97,6 @@ public abstract class AF3FXViewPart extends FXViewPart ...@@ -127,36 +97,6 @@ public abstract class AF3FXViewPart extends FXViewPart
// Not needed. // Not needed.
} }
/**
* 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(getFXMLLocation() != 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 * 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, * code is based on SWT which uses a platform native look. Currently, Windows 8&10, Windows 7,
...@@ -200,10 +140,4 @@ public abstract class AF3FXViewPart extends FXViewPart ...@@ -200,10 +140,4 @@ public abstract class AF3FXViewPart extends FXViewPart
} }
return false; return false;
} }
/** {@inheritDoc} */
@Override
public Node getLayout() {
return root;
}
} }
CompositeFXControllerBase.java 7b4d3b7511e4ef146528cb2095856e4aa43e965f GREEN CompositeFXControllerBase.java 82fa7fdeb260e7469df2f7140a16b9b4201716cd YELLOW
ICompositeFXController.java d2c239d857aa1b125097fe0feb8ebb7dfe7f7072 GREEN ICompositeFXController.java 8da8c709bc6a8232f7d9dfc0a4e76cea2d6f6879 YELLOW
...@@ -15,6 +15,10 @@ ...@@ -15,6 +15,10 @@
+--------------------------------------------------------------------------*/ +--------------------------------------------------------------------------*/
package org.fortiss.tooling.common.ui.javafx.layout; package org.fortiss.tooling.common.ui.javafx.layout;
import static javafx.scene.layout.AnchorPane.setBottomAnchor;
import static javafx.scene.layout.AnchorPane.setLeftAnchor;
import static javafx.scene.layout.AnchorPane.setRightAnchor;
import static javafx.scene.layout.AnchorPane.setTopAnchor;
import static org.fortiss.tooling.common.ui.javafx.util.JavaFXUtils.loadFXML; import static org.fortiss.tooling.common.ui.javafx.util.JavaFXUtils.loadFXML;
import java.io.IOException; import java.io.IOException;
...@@ -22,6 +26,7 @@ import java.net.URL; ...@@ -22,6 +26,7 @@ import java.net.URL;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.layout.AnchorPane;
/** /**
* JavaFX node which can be hierarchically composed. It can be included within another * JavaFX node which can be hierarchically composed. It can be included within another
...@@ -37,19 +42,32 @@ public abstract class CompositeFXControllerBase implements ICompositeFXControlle ...@@ -37,19 +42,32 @@ public abstract class CompositeFXControllerBase implements ICompositeFXControlle
*/ */
private Node layout; private Node layout;
/**
* {@link ICompositeFXController}s to be added to the {@link Node} by the
* {@code addContainments} method.
*/
private ICompositeFXController[] containments;
/** Constructor. */ /** Constructor. */
public CompositeFXControllerBase(CompositeFXControllerBase... containments) throws IOException { public CompositeFXControllerBase(ICompositeFXController... containments) {
URL fxmlResource = getClass().getResource(getFXMLLocation()); this.containments = containments;
layout = loadFXML(fxmlResource, this); this.layout = null;
addContainments(containments);
} }
/** /** {@inheritDoc} */
* Returns a JavaFX {@link Node} with the layout specified in the {@link FXML} resource and
* associated with a controller of type {@code T}.
*/
@Override @Override
public Node getLayout() { public Node getOrLoadLayout() {
if(layout == null) {
URL fxmlResource = getClass().getResource(getFXMLLocation());
try {
layout = loadFXML(fxmlResource, this);
} catch(IOException e) {
throw new RuntimeException(
"Cannot load the resource located at " + getFXMLLocation(), e);
}
initialize();
addContainments(containments);
}
return layout; return layout;
} }
...@@ -60,4 +78,17 @@ public abstract class CompositeFXControllerBase implements ICompositeFXControlle ...@@ -60,4 +78,17 @@ public abstract class CompositeFXControllerBase implements ICompositeFXControlle
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public abstract void addContainments(ICompositeFXController[] containments); public abstract void addContainments(ICompositeFXController[] containments);
/**
* If the container is an {@link AnchorPane}, all margins are removed so as to fit the child to
* the anchor pane's size.
*/
protected void fitToParent(Node child) {
if(child.getParent() instanceof AnchorPane) {
setBottomAnchor(child, 0.0);
setTopAnchor(child, 0.0);
setLeftAnchor(child, 0.0);
setRightAnchor(child, 0.0);
}
}
} }
...@@ -26,14 +26,16 @@ import javafx.scene.Node; ...@@ -26,14 +26,16 @@ import javafx.scene.Node;
public interface ICompositeFXController { public interface ICompositeFXController {
/** /**
* Returns a JavaFX {@link Node} with the layout specified in the {@link FXML} resource and * Loads the a JavaFX {@link Node} from the specified {@link FXML} resource.
* associated with a controller of type {@code T}.
*/ */
public Node getLayout(); public Node getOrLoadLayout();
/** Returns the location of the {@link FXML} resource with the view's layout. */ /** Returns the location of the {@link FXML} resource with the view's layout. */
public String getFXMLLocation(); public String getFXMLLocation();
/** Adds the {@link ICompositeFXController}s to the container. */ /** Adds the {@link ICompositeFXController}s to the container. */
public void addContainments(ICompositeFXController[] containments); public void addContainments(ICompositeFXController[] containments);
/** Performs further actions on the layout. */
public void initialize();
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment