Ślad: • start • documentation • 12_spring • 09_javafx

JavaFX i OpenJFX

Writing /home/grochu/public_html/wiki/data/cache/c/c0a9c1eed3270d02d5fdcbd78d6b4a1f.i failed
Unable to save cache file. Hint: disk full; file permissions; safe_mode setting.
Writing /home/grochu/public_html/wiki/data/meta/zajecia/java/09_javafx.meta failed
Unable to save metadata file. Hint: disk full; file permissions; safe_mode setting.
Writing /home/grochu/public_html/wiki/data/cache/a/a78cbd33a6c694b3f51b5258479b5b5a.i failed
Writing /home/grochu/public_html/wiki/data/meta/zajecia/java/sidebar.meta failed
Writing /home/grochu/public_html/wiki/data/cache/c/c0a9c1eed3270d02d5fdcbd78d6b4a1f.i failed
Unable to save cache file. Hint: disk full; file permissions; safe_mode setting.

REVEAL theme=simple&disableLayout=0&transition=none&controls=1&show_progress_bar=1&build_all_lists=0&show_image_borders=0&horizontal_slide_level=2&enlarge_vertical_slide_headers=0&show_slide_details=1&open_in_new_window=1&size=1024×768

<html> <style type=„text/css”> p { text-align: left; } .reveal { font-size: 30px; } .reveal section { width: 96%; margin: 0 auto; } .reveal .slides { width: 60% !important; } .reveal ul, .reveal ol { margin-left: 1.5em; } .reveal pre code {

height: 100%;
max-height: 100%;
box-sizing: border-box;

} </style> </html>

JavaFX i OpenJFX

JavaFX - historia i pozycja w ekosystemie

  • JavaFX - zestaw bibliotek do tworzenia nowoczesnych aplikacji desktopowych w Javie
  • Od JDK 8 wbudowany w Oracle JDK jako nowoczesna alternatywa dla AWT/Swing
  • Od JDK 11 nie jest już częścią JDK, rozwijany osobno jako OpenJFX
  • OpenJFX - otwartoźródłowy projekt rozwijający JavaFX przez społeczność, Oracle i firmę Gluon
  • Aktualne wydania: Gluon OpenJFX

Cechy JavaFX

  • architektura oparta o graf Scene Graph
  • deklaratywne UI (FXML)
  • stylowanie CSS
  • multimedia, wsparcie dla animacji
  • obsługa grafiki 2D i 3D oraz renderowania sprzętowego (akceleracja GPU)
  • duża liczba wbudowanych kontrolek i możliwość rozbudowy
  • WebView, silnik WebKit do osadzania treści webowej
  • integracja z Swing (SwingNode)
  • mechanizm danych reaktywnych (Observable, Property, Binding)

Architektura JavaFX

Warstwy JavaFX

  • Prism - akcelerowany potok renderowania (GPU: DirectX/OpenGL).
    W przypadku braku wsparcia sprzętowego JavaFX przechodzi w tryb renderowania programowego.
  • Glass Windowing Toolkit - warstwa okien, zdarzeń i timerów; interfejs między JavaFX a systemem operacyjnym.
  • WebView - kontrolka osadzająca HTML/JS (silnik WebKit) umożliwiająca renderowanie i interakcję z treścią webową.
  • Media Engine - silnik multimediów (audio/video) obsługujący różne formaty i kodeki.
  • Quantum Toolkit - łączy warstwy niskopoziomowe z API JavaFX.
  • Scene Graph - drzewo węzłów UI i grafiki
  • JavaFX API - publiczne klasy i interfejsy do budowy interfejsu graficznego

JavaFX SDK API

  • javafx.base - podstawowe klasy, kolekcje, properties
  • javafx.graphics - grafika 2D/3D, scena, renderowanie
  • javafx.controls - gotowe kontrolki UI
  • javafx.fxml - FXML, loader, kontrolery
  • javafx.media - multimedia
  • javafx.web - WebView, silnik WebKit
  • javafx.swing - integracja z Swing (SwingNode)

Struktura aplikacji JavaFX

141_1.jpg

  • Stage - okno aplikacji - scena teatralna, gdzie odbywa się przedstawienie
  • Scene - zawartość okna - konkretna scenografia, rekwizyty i aktorzy ustawieni na scenie, zawiera hierarchię węzłów (Scene Graph)
  • Node - elementy UI, grafiki

Scene Graph

137_1.jpg

  • Każdy element UI (obiekty 2D/3D, kontrolki UI, kontenery, elementy multimedialne) jest węzłem grafu (Node)
  • Renderowanie, transformacje i zdarzenia propagują się po drzewie
  • Zmiana stanu węzła natychmiast widoczna w interfejsie

Elementy Scene Graph

  • Scene to główny kontener, w którym umieszczamy hierarchię węzłów
  • Korzeń (Root) - główny węzeł sceny, zwykle layout (np. VBox, BorderPane)
  • Węzły rodzicielskie (Parent) - zawierają inne węzły (dzieci)
    • elementy grupujące (Group),
    • układy (Pane, HBox, StackPane, GridPane)
    • kontrolki (Button, TextField, Label), też są rodzicami i mogą zawierać wewnętrzne węzły
  • Liście - elementy graficzne 2D/3D (Shape, ImageView, Text, SwingNode)
VBox root = new VBox();
Label title = new Label("Kurs JavaFX");
Button button = new Button("Kliknij");
root.getChildren().addAll(title, button);
 
Scene scene = new Scene(root, 600, 400);

Cykl życia aplikacji

  • Punkt wejścia: klasa dziedzicząca po Application
  • Kluczowa metoda: start(Stage stage) udostępniająca główny Stage.
    W metodzie start():
    • przygotowujemy scenę (tworzymy węzły, układamy je w layoutach)
    • ustawiamy Stage (tytuł, rozmiar, scenę)
    • wywołujemy show(), aby wyświetlić okno.
  • Uruchomienie: launch(args) w main()
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;
 
public class HelloFxApp extends Application {
    @Override
    public void start(Stage stage) {
        Scene scene = new Scene(new Label("Witaj JavaFX"), 400, 200);
        stage.setTitle("Hello FX");
        stage.setScene(scene);
        stage.show();
    }
 
    public static void main(String[] args) {
        launch(args);
    }
}

Konfiguracja JavaFX w IntelliJ

JavaFX nie jest już częścią JDK. Istnieje kilka sposobów na dodanie JavaFX do projektu:

  • instalacja ręczna - pobierz OpenJFX, dodaj biblioteki do projektu, ustaw VM options
  • z użyciem Maven lub Gradle - dodaj zależności do pom.xml lub build.gradle
  • szablon projektu JavaFX w IntelliJ (plugin JavaFX, używa Maven) - brakujące zależności powinny zostać pobrane automatycznie

Dokumentacja OpenJFX zawiera szczegółowe instrukcje dla różnych IDE i systemów budowania.

Ręczna konfiguracja JavaFX

  • Pobierz SDK JavaFX z Gluon lub Oracle zgodny z wersją JDK
  • Dodaj biblioteki JavaFX do projektu
    File → Project Structure → Libraries
  • Ustaw ścieżkę do modułu w VM options przy uruchamianiu:
    Build → Edit Configurations → VM options
    --module-path "ścieżka/do/javafx-sdk-XX/lib" --add-modules javafx.controls,javafx.fxml


Konfiguracja JavaFX z użyciem Maven

Maven - system zarządzania projektem i budowania, który automatycznie pobiera zależności i konfiguruje projekt. Jak skonfigurować JavaFX z Maven:

  1. Utwórz projekt Maven (lub użyj archetypu JavaFX)
  2. Wyszukaj JavaFX w repozytorium Maven Maven Central lub https://mvnrepository.com/
  3. Dodaj zależności javafx-controls (opcjonalnie javafx-fxml) do pliku pom.xml
  4. Skonfiguruj plugin javafx-maven-plugin do uruchamiania (zdarzenie javafx:run) oraz maven-compiler-plugin
  5. Uruchamiaj (z CLI) przez zadanie Maven, z okna narzędzi Maven IDE albo skonfiguruj IDE
    mvn javafx:run

JavaFX Maven Project in IntelliJ IDEA from Scratch

Przykładowa konfiguracja Maven dla JavaFX

<dependencies>
    <dependency>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-controls</artifactId>
        <version>26</version>
    </dependency>
    <dependency>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-fxml</artifactId>
        <version>26</version>
        <scope>compile</scope>
    </dependency>
</dependencies>

Pluginy Maven dla JavaFX

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.15.0</version>
            <configuration>
                <source>25</source>  <!-- Match your JDK version -->
                <target>25</target>
            </configuration>
        </plugin>
 
        <plugin>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-maven-plugin</artifactId>
            <version>0.0.8</version>
            <executions>
                <execution>
                    <id>default-cli</id>
                    <configuration>
                        <mainClass>MyJavaFXApp</mainClass>  <!-- Your main class -->
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

Uruchamianie aplikacji JavaFX z IDE

  • Opcja 1: Okno narzędzi Maven
    • Lifecycle → clean
    • Plugins → javafx → javafx:run
  • Opcja 2: Dodaj konfigurację uruchomienia w IntelliJ
    • Run → Edit Configurations
    • Dodaj + → Maven
    • W polu Command line wpisz: clean javafx:run

Wątki i JavaFX

  • JavaFX ma własny wątek UI (JavaFX Application Thread) uruchamiany automatycznie przy starcie aplikacji
  • Wszystkie operacje na węzłach muszą być wykonywane w tym wątku
  • Aby zachować responsywność, długotrwałe zadania powinny być wykonywane w osobnych wątkach, np. przez Task lub Service, a aktualizacje UI przez Platform.runLater()

Układy

Layouty

  • javafx.scene.layout - układy (layout) - specjalne kontenery, które automatycznie rozmieszczają swoje dzieci według określonych reguł
  • HBox, VBox, ButtonBar - układ poziomy/pionowy, rozmieszcza dzieci w linii
  • BorderPane - układ z pięcioma regionami (top, bottom, left, right, center)
  • SplitPane - układ dzielący przestrzeń na panele o regulowanej wielkości
  • GridPane - układ siatkowy, rozmieszcza dzieci w wierszach i kolumnach
  • FlowPane - układ przepływowy, rozmieszcza dzieci w linii, zawija do następnego wiersza
  • AnchorPane - układ kotwiczący, pozwala przypiąć dzieci do krawędzi
  • StackPane - nakłada dzieci jedno na drugie, centrowanie
StackPane stackPane = new StackPane();
 
Ellipse ellipse = new Ellipse(110, 70);
ellipse.setFill(Color.LIGHTBLUE);
 
Text text = new Text("Hello World");
text.setFont(new Font("Arial Bold", 24));
 
stackPane.getChildren().addAll(ellipse, text);
Scene scene = new Scene(stackPane, 350, 230, Color.LIGHTYELLOW);

Efekty graficzne

  • Effect - wbudowane efekty graficzne do stylizacji węzłów:
    • DropShadow - efekt cienia, można dostosować kolor, rozmycie i przesunięcie
    • Reflection - efekt odbicia, można dostosować wysokość i rozmycie
  • Wypełnienie kształtu:
    • Color, ImagePattern, RadialGradient
    • LinearGradient - gradient liniowy, definiowany przez punkty początkowy i końcowy oraz zestaw Stop (pozycja + kolor)
Stop[] stops = new Stop[] { 
        new Stop(0, Color.DODGERBLUE),
        new Stop(0.5, Color.LIGHTBLUE),
        new Stop(1.0, Color.LIGHTGREEN)  };
LinearGradient gradient = new LinearGradient(0, 0, 1, 1, true, CycleMethod.NO_CYCLE, stops);
ellipse.setFill(gradient);
 
ellipse.setEffect(new DropShadow(30, 10, 10, Color.GRAY));

Animacje

  • System animacji oparty na klasie Transition i Timeline
  • Można animować właściwości węzłów (pozycja, rozmiar, kolor) oraz tworzyć sekwencje animacji
  • Animacje są płynne dzięki renderowaniu sprzętowemu
  • FadeTransition, RotateTransition, ScaleTransition, TranslateTransition '' - gotowe klasy do animacji określonych właściwości * ''Timeline - bardziej elastyczna klasa do animowania dowolnych właściwości w określonych punktach czasowych
  • ParallelTransition i SequentialTransition - do łączenia wielu animacji
  • play() - uruchamia animację, stop() - zatrzymuje, pause() - wstrzymuje, getStatus() - sprawdza status animacji
RotateTransition rotate = new RotateTransition(Duration.millis(2500), stackPane);
rotate.setToAngle(360);
rotate.setFromAngle(0);
rotate.setInterpolator(Interpolator.LINEAR);
rotate.play();

Obsługa zdarzeń

  • Event - obsługa zdarzeń (myszy, klawiatury, akcji)
  • Można rejestrować nasłuchiwacze zdarzeń na węzłach grafu
  • Zdarzenia propagują się po drzewie, rodzic może przechwycić zdarzenie dziecka
  • setOnAction(EventHandler<ActionEvent>), setOnMouseClicked(EventHandler<MouseEvent>) dla kontrolek (węzłów) obsługujących konkretne zdarzenia,
  • addEventHandler(EventType<T>, EventHandler<T>) dla ogólnych zdarzeń
stackPane.setOnMouseClicked(mouseEvent -> {
    if (rotate.getStatus().equals(Animation.Status.RUNNING)) {
        rotate.pause();
    } else {
        rotate.play();
    }
});

Obserwowanie i wiązanie właściwości

  • JavaFX posiada wbudowany mechanizm danych reaktywnych, możemy wiązać właściwości węzłów z innymi właściwościami lub źródłami danych, tak aby zmiany były automatycznie odzwierciedlane w interfejsie
  • właściwości węzłów są obserwowalne np. textProperty() dla Label czy TextField, możemy ustawić nasłuchiwacz reagujący na zmiany lub powiązać je z innymi właściwościami
  • Observable oraz ObservableValue - interfejsy reprezentujące obserwowalną wartość, posiadają metody do dodawania nasłuchiwaczy addListener() i pobierania aktualnej wartości


Przykład

rotate.statusProperty().addListener(new InvalidationListener() {
    @Override
    public  void invalidated(Observable observable) {
        text.setText("Animation status: " +
        ((ObservableObjectValue<Animation.Status>)observable).getValue());
    }
});

lub krócej

rotate.statusProperty().addListener(observable -> {
    text.setText("Animation status: " + rotate.getStatus());
});

Binding

  • Binding pozwala na tworzenie dynamicznych powiązań między właściwościami implementującymi interfejs Property<T> bez potrzeby dodawania ręcznych nasłuchiwaczy
  • bind() - tworzy jednokierunkowe powiązanie, gdzie jedna właściwość jest zależna od drugiej
text.rotateProperty().bind(stackPane.rotateProperty());
  • przekształcanie wartości do tworzenia złożonych powiązań, np. matematycznych operacji, warunków logicznych, formatowania tekstu
text2.textProperty().bind(stackPane.rotateProperty().asString("%.1f"));
  • łączenie wielu właściwości i operacji na nich, np. konkatenacja tekstu z dwóch TextField do Label
TextField firstName = new TextField();
TextField lastName = new TextField();
Label fullName = new Label();
 
fullName.textProperty().bind(
    firstName.textProperty().concat(" ").concat(lastName.textProperty())
);

Wiązanie dwukierunkowe

  • bindBidirectional() - dwukierunkowe powiązanie, zmiana jednej właściwości automatycznie aktualizuje drugą i odwrotnie
  • przydatne w formularzach, gdzie chcemy, aby zmiany w UI były odzwierciedlane w modelu danych i na odwrót
firstName.textProperty().bindBidirectional(model.firstNameProperty());

FXML - deklaratywny język UI

  • FXML to XML opisujący hierarchię UI, pozwala na oddzielenie logiki od warstwy prezentacji
  • FXMLLoader wczytuje i parsuje plik FXML, tworzy obiekty UI i ustawia je w hierarchii
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>  
 
<VBox xmlns:fx="http://javafx.com/fxml">
    <Button text="OK" />
    <Label text="Witaj JavaFX" />
</VBox>
FXMLLoader loader = new FXMLLoader(getClass().getResource("main-view.fxml"));
Parent root = loader.load();
Scene scene = new Scene(root);
stage.setScene(scene);

Scene Builder


Scene Builder in Action

Elementy FXML

  • Instancje klas, np. <Button> tworzy obiekt klasy javafx.scene.control.Button
  • Własności obiektów (settery, kolekcje)
    <Button>
        <text>Hello, World!</text>
    </Button>
  • Własności statyczne, np. GridPane.rowIndex
    <Label text="My Label">
      <GridPane.rowIndex>0</GridPane.rowIndex>
      <GridPane.columnIndex>0</GridPane.columnIndex>
    </Label>
  • Element <children> jest używany do definiowania listy dzieci
    <VBox>
        <children>
            <Button text="OK" />
            <Label text="Witaj JavaFX" />
        </children>
    </VBox>
  • Klasy mogą definiować domyślne własności, np. <children> dla węzłow rodzicielskich (np. VBox i HBox)
    <VBox>
        <Button text="OK" />
        <Label text="Witaj JavaFX" />
    </VBox>

Atrybuty FXML

  • Atrybuty elementów mapują się na właściwości obiektów, np. text=„OK” dla Button ustawia właściwość text obiektu Button
      <Button text="OK" />
     
  • Można używać atrybutów do ustawiania prostych wartości, takich jak tekst, kolory, rozmiary, ale bardziej złożone struktury (np. hierarchia węzłów) wymagają elementów zagnieżdżonych
  • Ustawianie własności statycznych
      <Label text="My Label" GridPane.rowIndex="0" GridPane.columnIndex="0"/>
     
  • Specjalne atrybuty FXML:
    • fx:controller - określa klasę kontrolera powiązaną z tym widokiem
    • fx:id - identyfikator węzła, pozwala m.in. na powiązanie z polem kontrolera
    • onAction - przypisuje metodę obsługi zdarzenia (np. kliknięcia) z kontrolera
  • Powiązanie z własnością kontrolera
    <Label text="${controller.someProperty}" />


    Atrybut text jest ustawiany na wartość właściwości someProperty z kontrolera, a aktualizacje tej właściwości będą automatycznie odzwierciedlane w interfejsie.


FXML + Controller

  • Wzorzec MVC: pliki FXML definiują strukturę interfejsu (widok), a kontrolery Java obsługują logikę i zdarzenia


// MainController.java
public class MainController {
    @FXML 
    private TextField input;
    @FXML 
    private Label output;
 
    @FXML
    private void onGenerate() {
        output.setText("Witaj, " + input.getText().trim());
    }
}
<!-- main-view.fxml -->
<VBox spacing="8" xmlns:fx="http://javafx.com/fxml" fx:controller="demo.MainController">
    <TextField fx:id="input" promptText="Podaj imię" />
    <Button text="Generuj" onAction="#onGenerate" />
    <Label fx:id="output" />
</VBox>

Inicjalizacja kontrolera

  • Metoda initialize() interfejsu Initializable w kontrolerze jest wywoływana automatycznie po załadowaniu FXML i wstrzyknięciu zależności
  • Można jej użyć do dodatkowej konfiguracji elementów UI, ustawienia nasłuchiwaczy, inicjalizacji danych
public class MainController implements Initializable {
    @FXML private TextField input;
    @FXML private Label output;
 
    @Override
    public void initialize(URL location, ResourceBundle resources) {
        input.setText("Jan");
        output.setText("Witaj, " + input.getText());
    }
}

FXMLLoader

  • FXMLLoader - klasa odpowiedzialna za wczytywanie i parsowanie plików FXML, tworzenie obiektów UI i hierarchii
    • load() - wczytuje FXML i zwraca korzeń hierarchii (Parent)
    • getController() - zwraca instancję kontrolera powiązanego z tym FXML
    • setLocation(URL) - ustawia lokalizację FXML
    • setResources(ResourceBundle) - ustawia zasoby (dla internacjonalizacji)


URL location = getClass().getResource("example.fxml");
ResourceBundle resources = ResourceBundle.getBundle("com.foo.example");
FXMLLoader fxmlLoader = new FXMLLoader(location, resources);
 
Pane root = (Pane)fxmlLoader.load();
MyController controller = (MyController)fxmlLoader.getController();

Metoda load()

  1. Odczyt pliku FXML, tworzenie obiektów i ustawienie właściwości węzłów
  2. Utworzenie instancji kontrolera (jeśli fx:controller jest określony)
  3. Wstrzyknięcie zależności do kontrolera (pola publiczne lub oznaczone @FXML i odpowiadające im fx:id w FXML)
  4. Rejestrowanie nasłuchiwaczy zdarzeń (np. onAction=#methodName)
  5. Wywołanie metody initialize() kontrolera (jeśli implementuje Initializable lub posiada metodę oznaczoną @FXML o nazwie initialize)

CSS w JavaFX

  • Stylowanie podobne do webowego CSS, ale z prefiksem -fx-
  • Plik CSS podpinamy pod scenę lub dowolnego węzła rodzicielskiego:
      scene.getStylesheets().add(getClass().getResource("Style.css").toExternalForm()); 
  • Style można dodać w FXML
    <stylesheets>
       <URL value="@demo.css" />
    </stylesheets>
     
    <VBox stylesheet="@Style.css">
       <Button text="Submit" id="primaryButton" styleClass="primaryButton"/>
    </VBox>
  • Style propagują się w dół hierarchii
  • domyslny plik CSS dla JavaFX: modena.css - zawiera style dla wszystkich wbudowanych kontrolek, można go nadpisać lub rozszerzyć

Powiązanie stylów z węzłami

  • Bezpośredni styl dla pojedynczego węzła (style inline)
    button.setStyle("-fx-font-size: 16px; ");
  • Selektory CSS w pliku CSS (klasy, identyfikatory) pozwalają na grupowanie i ponowne użycie stylów
  • Style można określić w FXML (właściwości styleClass i id) i konfigurować w Scene Builder
    <Label text="Hello" styleClass="greeting"/>
    <Button text="Submit" id="primaryButton"/>
  • Style można określić w kodzie Java, dodając klasy CSS do węzłów lub ustawiając identyfikatory
label.getStyleClass().add("greeting");
button.setId("primaryButton");

Selektry CSS JavaFX

/* Plik CSS dla JavaFX */
/* Selekcja po nazwie węzła */
Button {
    -fx-background-color: lightblue;
    -fx-text-fill: darkblue;
}
 
/* Selekcja po klasie CSS */
.primaryButton {
    -fx-background-color: linear-gradient(to right, #ff7e5f, #feb47b);
    -fx-text-fill: white;  
    -fx-font-size: 16px;
    -fx-padding: 10px 20px;
    -fx-background-radius: 8px;
}
 
/* Selekcja po identyfikatorze */
#primaryButton {
    -fx-background-color: linear-gradient(to right, #ff7e5f, #feb47b);
    -fx-text-fill: white;   
    -fx-font-size: 16px;
    -fx-padding: 10px 20px;
    -fx-background-radius: 8px;
}
 
/* Selekcja pseudoklasy */
Button:hover {
    -fx-background-color: linear-gradient(to right, #feb47b, #ff7e5f);
}

Priorytety stylowania

  • Styl inline
  • Identyfikator CSS (#id)
  • Klasa CSS (.class)
  • Styl domyślny węzła

Materiały

Zadanie 8: Lista zakupów

Stwórz prostą aplikację JavaFX do zarządzania listą zakupów.

Wymagania:

  • Aplikacja powinna umożliwiać dodawanie nowych pozycji do listy oraz usuwanie zaznaczonych pozycji.
  • Użyj ListView do wyświetlania listy, TextField do wprowadzania nowych pozycji
  • Przycisk Dodaj powinien być aktywny tylko wtedy, gdy pole tekstowe nie jest puste (użyj wiązania)
  • Przycisk Usuń powinien usuwać aktualnie zaznaczoną pozycję z listy (lub wiele zaznaczonych pozycji)
  • Zadeklaruj widok w FXML. Wygląd aplikacji dowolny. Zaprojektuj samodzielnie, najlepiej z pomocą Scene Buildera
  • Zdefiniuj klasę kontrolera i powiąż go z widokiem
  • Zastosuj stylowanie CSS do poprawy wyglądu interfejsu

Wskazówki:

  • disableProperty().bind(…) dla przycisku
  • getSelectionModel().getSelectedIndex() dla usuwania
  • Puste wejście ignoruj przez trim().isEmpty()