Skip to content
Snippets Groups Projects
Commit bf9c99cf authored by Simon Barner's avatar Simon Barner
Browse files

Merge branch 'master' of https://git.fortiss.org/af3/kernel.git into...

Merge branch 'master' of https://git.fortiss.org/af3/kernel.git into 3401-developer_documentation_kernel
parents 484aa460 311c7131
No related branches found
No related tags found
1 merge request!113401 developer documentation kernel
AF3FXViewPart.java 9e119a36de6ab9e1a07cd416de0126a89514bd38 GREEN
AF3FXViewPart.java 96871186e27ace44069441dbf7ac8ebcd7753390 GREEN
......@@ -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) {
......
AutoUndoCommandStack.java 6aa645a9ed6e6547539c376fda97284928c4f9d4 GREEN
EMFTransactionalCommand.java ba4b5bead9768b6ce6c955b9238cd96cb722533c GREEN
EclipseResourceStorageService.java 1b9722e31a5ec33e4c3f7bb171fc2ce587729bf8 GREEN
ModelContext.java 85ede185d4b232221a96d0bf37a4c676b105dc00 GREEN
ModelContext.java 220944231c0e036f29d4911d2658c33b2427b09f GREEN
NonDirtyingEMFTransactionalCommand.java ec5f282603891096b09f2628155dd27e3a21c588 GREEN
......@@ -34,6 +34,7 @@ import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.eclipse.core.resources.IFile;
......@@ -151,7 +152,14 @@ class ModelContext implements ITopLevelElement, CommandStackListener {
}
});
checkIDs();
if(checkIDs()) {
try {
doSave(new NullProgressMonitor());
} catch(IOException | CoreException e) {
error(ToolingKernelActivator.getDefault(),
"Error saving model file when fixing missing/duplicate IDs.", e);
}
}
}
/** {@inheritDoc} */
......@@ -182,29 +190,35 @@ class ModelContext implements ITopLevelElement, CommandStackListener {
}
/**
* Checks whether all IDs are present and unique and updates {@link #maxId}. In case duplicate
* IDs have been detected (which could only be caused by a bug in AF3), these are removed and an
* error is logged.
* Checks whether all IDs are present and unique and updates {@link #maxId}.
* <ul>
* <li>Duplicate IDs indicate a programming error. They are removed and an error is logged</li>
* <li>Missing IDs are silently added.</li>
* </ul>
*
* @return {@code true} iff the model was modified because of missing/duplicated IDs (and hence
* must
* be saved).
*/
private void checkIDs() {
private boolean checkIDs() {
Set<Integer> ids = new HashSet<Integer>();
boolean hadMissing = false;
boolean hadDuplicates = false;
// Store String representation of element with duplicated IDs before fixing it
Map<String, Integer> objWithIdProblem = new HashMap<String, Integer>();
for(Iterator<EObject> i = getRootModelElement().eAllContents(); i.hasNext();) {
EObject eo = i.next();
if(eo instanceof IIdLabeled) {
final IIdLabeled element = (IIdLabeled)eo;
int id = element.getId();
if(id <= 0) {
hadMissing = true;
objWithIdProblem.put(element.toString(), null);
} else {
// Reset duplicate IDs. They will be regenerated together with missing IDs. This
// is a safe operation since at this stage, references are still based on Java
// references. Only in the persisted resource, references will be based on IDs,
// which is why special care must be taken to ensure uniqueness.
if(ids.contains(id)) {
hadDuplicates = true;
objWithIdProblem.put(element.toString(), element.getId());
runAsNonDirtyingCommand(() -> {
element.setId(0);
});
......@@ -218,24 +232,31 @@ class ModelContext implements ITopLevelElement, CommandStackListener {
maxId = max(0, maxId);
if(hadMissing || hadDuplicates) {
if(!objWithIdProblem.isEmpty()) {
runAsNonDirtyingCommand(() -> {
maxId = generateMissingIDs(getRootModelElement(), maxId);
});
try {
doSave(new NullProgressMonitor());
} catch(IOException | CoreException e) {
error(ToolingKernelActivator.getDefault(),
"Error saving model file when fixing missing/duplicate IDs.", e);
}
}
if(hadDuplicates) {
String msg = "Duplicate IDs have been removed from \"";
msg += resource.getURI().lastSegment() + "\". ";
msg += "Please report this incident since it could result in corrupted model files.";
String msg = "The following missing/duplicate IDs have been fixed in \"";
msg += resource.getURI().lastSegment() + "\".\n";
msg += "Please report this incident since it indicates a programming error ";
msg += "that could result in corrupted model files.\n";
for(Entry<String, Integer> entry : objWithIdProblem.entrySet()) {
Integer id = entry.getValue();
if(id == null) {
msg += " Missing ID added to ";
} else {
msg += " Duplicate ID " + id + " disambiguated for ";
}
String name = entry.getKey();
msg += "element \"" + name + "\"\n";
}
error(ToolingKernelActivator.getDefault(), msg);
return true;
}
return false;
}
/** {@inheritDoc} */
......
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