Das für Visualisierung und Event-Handling eingesetzte Framework für CMF 3.0 ist JavaFX (11 oder höher) - siehe https://openjfx.io

Nachfolgend sind einige Grundlagen aus JavaFX zusammengefasst und jeweils auf die Anwendung in CMF 3.0 bezogen.

Basics

Die wichtigsten Konzepte in JavaFX sind

Eine Stage ist im Prinzip ein Fenster, das als „Bühne“ für unseren Inhalt dient. In der Stage enthalten ist ein Objekt der Klasse Scene.

Die Scene („Szene“) enthält alle Objekte, die in der Stage gerendert werden sollen, sowie interne Werte, auf die zugegriffen werden kann.

Die Objekte, die in der Stage gerendert werden sollen, sind alle vom Typ Node abgeleitet.

Das ganze heisst auch "Scene-Graph"

TBD

TBD: Background image - wie passt das da rein

Wir verwenden keine LayoutManger - die Objekte werden direkt gesetzt ... TBD

Nodes

Nodes sind primitive Objekte in JavaFX

Nodes können nicht nur gerendert werden, alle Nodes lassen sich auch bewegen, skalieren, drehen oder scheren. Außerdem lassen sich auf Nodes visuelle Effekte anwenden.

Siehe: https://openjfx.io/javadoc/11/javafx.graphics/javafx/scene/Node.html

Koordinatensystem

In einem Node wird ein klassisches lokales Koordinatensystem genutzt - mit einer X-Achse, die nach rechts wächst und einer Y-Achse, die nach unten wächst.

Auf Nodes können Transformations angewendet werden (rotation, translation and scaling) - diese wirken innerhalb des lokalen Koordinatensystems.

Deshalb

"The node's final translation will be computed as layoutX + translateX, where layoutX establishes the node's stable position and translateX optionally makes dynamic adjustments to that position."

Es gibt folglich zwei Möglichkeiten, einen Node zu platzieren / zu verschieben:

Für die VisualComponents ergibt sich folgendes:

CSS

The Node class contains id, styleClass, and style variables that are used in styling this node from CSS. The id and styleClass variables are used in CSS style sheets to identify nodes to which styles should be applied. The style variable contains style properties and values that are applied directly to this node.

See

CSS is currently used in the CMF3 to style designs in FXML files - see there.

Groups

Group node contains an ObservableList of children that are rendered in order whenever this node is rendered.

Group will take on the collective bounds of its children and is not directly resizable.

Any transform, effect, or state applied to a Group will be applied to all children of that group. Such transforms and effects will NOT be included in this Group's layout bounds, however if transforms and effects are set directly on children of this Group, those will be included in this Group's layout bounds.

By default, a Group will "auto-size" its managed resizable children to their preferred sizes during the layout pass to ensure that Regions and Controls are sized properly as their state changes. If an application needs to disable this auto-sizing behavior, then it should set autoSizeChildren to false and understand that if the preferred size of the children change, they will not automatically resize (so buyer beware!).

Animations

JavaFX unterstützt Animationen - dabei wird eine Strategie vorgegeben und dann über die vorgegebene Zeit per Transformations auf den Node angewandt.

z.B.

double fromX = visualComponent.getLayoutBounds().getMinX() + visualComponent.getLayoutBounds().getWidth() / 2;
double fromY = visualComponent.getLayoutBounds().getMinY() + visualComponent.getLayoutBounds().getHeight() / 2;
fromX += visualComponent.getTranslateX();
fromY += visualComponent.getTranslateY();
Path path = new Path();
path.getElements().add(new MoveTo(fromX, fromY));
path.getElements().add(new LineTo(fromX+diffX, fromY+diffY));
PathTransition pathTransition = new PathTransition();
pathTransition.setDuration(Duration.millis(500));
pathTransition.setPath(path);
pathTransition.setNode(visualComponent);
pathTransition.setOrientation(PathTransition.OrientationType.NONE);
pathTransition.setCycleCount(1);
pathTransition.setAutoReverse(true);
pathTransition.play();


Events

MouseEvent#getX() returns the x-coordinate relative to the source node. MouseEvent#getSceneX() returns the x-coordinate relative to the source node's scene. And MouseEvent#getScreenX() returns the x-coordinate relative to the entire screen

FXML

Die Model-View-Controller-Idee wird folgendermaßen umgesetzt:

Hinweis: Die MVC-Umsetzung im CMF3 ist nicht "ganz sauber", denn die Klasse FlowVisualItem definiert Callbacks für Touch und MouseEvents etc - übernimmt also einige Aufgaben eines Controllers.

CSS in FXML

CSS (see earlier) can be used on JavaFX nodes to style them in FXML files.

In CMF3 we load a default CSS file - additional CSS files from the theme can be loaded via  stylesheets="@../../flow.css"  in the FXML file.

Threads

Die JavaFX-Node-Klassen sind nicht threadsafe - d.h. sie können nur von einem Thread - dem JavaFX Application Thread genutzt werden.

Wenn nun (z.B. in Event-Händlern) länger dauernde Aktivitäten benötigt werden, dann ist dafür ein eigener Thread zu starten - Problem: In dem Thread können keine Node-Objekte genutzt werden - dafür ist es notwendig, die Update-Aktivitäten wieder in den Application-Thread zu schieben:

Platform.runLater(new Runnable() {
	@Override
	public void run() {
		addTeaserComponent(item, finalUrl);
	}
});

Beispiel einer komplexeren Nutzung:

Task task = new Task<Void>() {
	@Override public Void call() {
   	// load image to cache
   	ImageCache.fetchImageImmediately(finalUrl);
	// and now the stuff that has to be executed in the JavaFX thread
	Platform.runLater(new Runnable() {
		@Override
		public void run() {
			addTeaserComponent(item, finalUrl);
		}
	});
    return null;
    }
};
new Thread(task).start();

Any computation intensive tasks must be decoupled from the JavaFX's main application thread by using separate worker threads. JavaFX provides a complete package to deal with the issues of multithreading and concurrency. There is an interface called Worker, an abstract class called Task, and ScheduledService for this purpose. The Task is basically a Worker implementation, ideal for implementing long running computation. The Task class extends FutureTask, and, as a result, supports Runnable, Future, and RunnableFuture interfaces as well. Due to its legacy/inheritance, this class can be used in various ways.

https://docs.oracle.com/javafx/2/threads/jfxpub-threads.htm

Andere Nutzung von Threads in ImageCache:

 this.executor = Executors.newFixedThreadPool(MAX_THREADS, runnable -> {
            Thread t = new Thread(runnable);
            t.setDaemon(true);
            return t ;
        });
 public Future<Image> doFetchImage(String identifier) {
        Task<Image> task = new Task<Image>() {
            @Override
            protected Image call() throws Exception {
                updateTitle("Image Cache Loader");
                //Check if matching Image is in cache
                Image image = getCachedImage(identifier);
                if(image != null) {
                	return image;
                }
                //Create new Image
                image = new Image(identifier, false);
                addToCache(identifier, image);
                return image;
            }
        };
        executor.execute(task);
        return task;
    }


Erweiterungen

Physics Library / Engine für JavaFX

Noch nicht näher betrachtet ...

http://www.jbox2d.org (https://github.com/jbox2d/jbox2d) - wird benutzt in https://github.com/AlmasB/FXGL

https://netopyr.com/2013/03/06/javafx-library-for-inverse-kinematics-2-0/

Veraltet?

http://www.cokeandcode.com/phys2d/