diff --git a/fx/ideas.md b/fx/ideas.md new file mode 100644 index 0000000..8c180d5 --- /dev/null +++ b/fx/ideas.md @@ -0,0 +1,15 @@ +# Ideen + +# Bindings +Bindngs kommen über ein spezielles Binding Control, das dann notwendige Bindings beinhaltet. + +Da kann dann auch eine eigene Logik zur Erkennung des Bindings erfolgen oder zusätziche Informationen bezüglich notwendiger Elemente in dem ViewModel + +# FXMLComponent +Dient dem Laden einer Komponente und bekommt dazu das fxml, die Daten und ggf. auch eine ModelView. + +# ModelView generieren +Damit eine ModelView nicht ständig manuell generiert werden muss, ist hier ggf. etwas zu generieren? +Ggf. eine eigenes Beschreibungssprache? + + diff --git a/fx/src/main/java/de/neitzel/fx/component/controls/Binding.java b/fx/src/main/java/de/neitzel/fx/component/controls/Binding.java new file mode 100644 index 0000000..f4dc831 --- /dev/null +++ b/fx/src/main/java/de/neitzel/fx/component/controls/Binding.java @@ -0,0 +1,67 @@ +package de.neitzel.fx.component.controls; + +import de.neitzel.fx.component.model.BindingData; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.scene.layout.Region; + +/** + * The BindingControl class represents a UI control that manages a list + * of {@link BindingData} objects. It extends the {@link Region} class and + * provides functionality to bind and monitor connections between source + * and target properties. + * + * The primary purpose of this control is to maintain an observable list + * of bindings, allowing developers to track or adjust the linked properties + * dynamically. + * + * The internal list of bindings is implemented as an {@link ObservableList}, + * allowing property change notifications to be easily monitored for UI + * updates or other reactive behaviors. + * + * This class serves as an organizational component and does not provide + * any user interaction by default. + */ +public class Binding extends Region { + + /** + * Represents an observable list of {@link BindingData} objects contained within the + * {@link Binding} instance. This list is utilized to manage and monitor + * the bindings between source and target properties dynamically. + * + * The list is implemented as an {@link ObservableList}, allowing changes in the + * collection to be observed and reacted to, such as triggering UI updates or + * responding to binding modifications. + * + * This field is initialized as an empty list using {@link FXCollections#observableArrayList()}. + * It is declared as final to ensure its reference cannot be changed, while the + * contents of the list remain mutable. + */ + private final ObservableList bindings = FXCollections.observableArrayList(); + + /** + * Retrieves the observable list of {@code Binding} objects associated with this control. + * The returned list allows monitoring and management of the bindings maintained + * by the {@code BindingControl}. + * + * @return an {@code ObservableList} containing the bindings managed by this control + */ + public ObservableList getBindings() { + return bindings; + } + + /** + * Constructs a new instance of the BindingControl class. + * + * This default constructor initializes the BindingControl without + * any pre-configured bindings. The instance will contain an empty + * {@link ObservableList} of {@link BindingData} objects, which can later + * be populated as needed. + * + * The constructor does not perform additional setup or initialization, + * allowing the class to be extended or customized as necessary. + */ + public Binding() { + // Absichtlich leer – wird später "ausgewertet" + } +} \ No newline at end of file diff --git a/fx/src/main/java/de/neitzel/fx/component/controls/FxmlComponent.java b/fx/src/main/java/de/neitzel/fx/component/controls/FxmlComponent.java new file mode 100644 index 0000000..a871db4 --- /dev/null +++ b/fx/src/main/java/de/neitzel/fx/component/controls/FxmlComponent.java @@ -0,0 +1,90 @@ +package de.neitzel.fx.component.controls; + +import de.neitzel.fx.component.ComponentLoader; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.scene.Node; +import javafx.scene.Parent; +import javafx.scene.layout.StackPane; + +import java.io.IOException; +import java.util.Arrays; + +public class FxmlComponent extends StackPane { + + private final StringProperty fxml = new SimpleStringProperty(); + private final StringProperty direction = new SimpleStringProperty("unidirectional"); + private final ObjectProperty data = new SimpleObjectProperty<>(); + + public StringProperty fxmlProperty() { return fxml; } + public String getFxml() { return fxml.get(); } + public void setFxml(String fxml) { this.fxml.set(fxml); } + + public StringProperty directionProperty() { return direction; } + public String getDirection() { return direction.get(); } + public void setDirection(String direction) { this.direction.set(direction); } + + public ObjectProperty dataProperty() { return data; } + public Object getData() { return data.get(); } + public void setData(Object data) { this.data.set(data); } + + public FxmlComponent() { + fxml.addListener((obs, oldVal, newVal) -> load()); + data.addListener((obs, oldVal, newVal) -> injectData()); + } + + private void load() { + if (getFxml() == null || getFxml().isBlank()) return; + try { + ComponentLoader loader = new ComponentLoader(); + // Option: ControllerFactory verwenden, wenn nötig + Parent content = loader.load(getClass().getResource(getFxml())); + + getChildren().setAll(content); + + Object controller = loader.getController(); + if (controller != null && getData() != null) { + injectDataToController(controller, getData()); + } + + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void injectData() { + if (!getChildren().isEmpty() && getData() != null) { + Object controller = getControllerFromChild(getChildren().get(0)); + if (controller != null) { + injectDataToController(controller, getData()); + } + } + } + + private void injectDataToController(Object controller, Object dataObject) { + // Daten-Objekt per Reflection zuweisen + // Beispiel: Controller hat `setData(User data)` + Arrays.stream(controller.getClass().getMethods()) + .filter(m -> m.getName().equals("setData") && m.getParameterCount() == 1) + .findFirst() + .ifPresent(method -> { + try { + method.invoke(controller, dataObject); + } catch (Exception e) { + e.printStackTrace(); + } + }); + + // Optional: automatisch Binding ausführen (s. u.) + } + + private Object getControllerFromChild(Node node) { + if (node.getProperties().containsKey("fx:controller")) { + return node.getProperties().get("fx:controller"); + } + return null; + } +} + diff --git a/fx/src/main/java/de/neitzel/fx/component/model/BindingData.java b/fx/src/main/java/de/neitzel/fx/component/model/BindingData.java new file mode 100644 index 0000000..6c7ba64 --- /dev/null +++ b/fx/src/main/java/de/neitzel/fx/component/model/BindingData.java @@ -0,0 +1,82 @@ +package de.neitzel.fx.component.model; + +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; + +/** + * The Binding class represents a connection between a source and a target, + * with an associated direction. It is typically used to define a binding + * relationship that determines how data flows between these two entities. + * + * The class provides three properties: + * - direction: Represents the type of the binding. Defaults to "unidirectional". + * - source: Represents the source of the binding. + * - target: Represents the target of the binding. + * + * It uses JavaFX property types, allowing these properties to be observed + * for changes. + */ +public class BindingData { + /** + * Represents the direction of the binding in the {@code Binding} class. + * It determines whether the binding is unidirectional or bidirectional. + * The default value is "unidirectional". + * + * This property is observed for changes, enabling dynamic updates + * within the JavaFX property system. + */ + private StringProperty direction = new SimpleStringProperty("unidirectional"); + /** + * Represents the source of the binding. This property holds a string value + * that specifies the originating object or identifier in the binding connection. + * It can be observed for changes, allowing updates to the binding relationship + * when the source value is modified. + */ + private StringProperty source = new SimpleStringProperty(); + /** + * Represents the target of the binding in the Binding class. + * This property holds the target value, which can be observed for changes. + */ + private StringProperty target = new SimpleStringProperty(); + + /** + * Gets the current direction of the binding. + * The direction determines how data flows between + * the source and the target, and typically defaults to "unidirectional". + * + * @return the current direction of the binding + */ + public String getDirection() { return direction.get(); } + /** + * Sets the direction of the binding. + * + * @param dir the new direction to set for the binding + */ + public void setDirection(String dir) { direction.set(dir); } + + /** + * Gets the current source value of the binding. + * + * @return the source value as a String. + */ + public String getSource() { return source.get(); } + /** + * Sets the value of the source property for this binding. + * + * @param source the new source value to be set + */ + public void setSource(String source) { this.source.set(source); } + + /** + * Retrieves the current value of the target property. + * + * @return the value of the target property as a String. + */ + public String getTarget() { return target.get(); } + /** + * Sets the target property of the binding. + * + * @param target the new value to set for the target property + */ + public void setTarget(String target) { this.target.set(target); } +} \ No newline at end of file