JavaFX Controls Grundlagen — Labels, Texte, Buttons, etc.

in Arbeit …​

1. Labels

Klasse javafx.scene.control.Label Javadoc 17 :
Label is a non-editable text control. A Label is useful for displaying text that is required to fit within a specific space, and thus may need to use an ellipsis or truncation to size the string to fit. Labels also are useful in that they can have mnemonics which, if used, will send focus to the Control listed as the target of the labelFor property.

2. TextField

Klasse javafx.scene.control.TextField Javadoc 17 :
Text input component that allows a user to enter a single line of unformatted text. Unlike in previous releases of JavaFX, support for multi-line input is not available as part of the TextField control, however this is the sole-purpose of the TextArea control. Additionally, if you want a form of rich-text editing, there is also the HTMLEditor control. TextField supports the notion of showing prompt text to the user when there is no text already in the TextField (either via the user, or set programmatically). This is a useful way of informing the user as to what is expected in the text field, without having to resort to tooltips or on-screen labels.

3. TextArea

Klasse javafx.scene.control.TextArea Javadoc 17 :
Text input component that allows a user to enter multiple lines of plain text. Unlike in previous releases of JavaFX, support for single line input is not available as part of the TextArea control, however this is the sole-purpose of the TextField control. Additionally, if you want a form of rich-text editing, there is also the HTMLEditor control.
TextArea supports the notion of showing prompt text to the user when there is no text already in the TextArea (either via the user, or set programmatically). This is a useful way of informing the user as to what is expected in the text area, without having to resort to tooltips or on-screen labels.

Example:

 var textArea = new TextArea("Lorem ipsum dolor sit amet, consectetur adipiscing elit, "
        + "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim "
        + "ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip "
        + "ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate "
        + "velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat "
        + "cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.");
 textArea.setWrapText(true);

4. Button

Klasse javafx.scene.control.Button Javadoc 17 :
A simple button control. The button control can contain text and/or a graphic. A button control has three different modes
…​
When a button is pressed and released a ActionEvent is sent. Your application can perform some action based on this event by implementing an EventHandler to process the ActionEvent. Buttons can also respond to mouse events by implementing an EventHandler to process the MouseEvent.
…​

5. Styling

in Arbeit ...

Als Nachschlage-Quelle kann u.a. die JavaFX-CSS-Referenz des Projekts verwendet werden: OpenJFX.IO Projekt — JavaFX 17 CSS Reference Guide

6. Beispiel MenuBar, TabView, TextArea, Prefs

Im Anschluss ein Beispiel, in dem unter Nutzung der im "Einstieg" gezeigten Funktionalitäten ein MenuBar, ein TabView mit einigen Tabs, eine TextArea implementiert werden. Auch eine erste Verwendung von Alert-Boxen ist enthalten.

Als App-Icon wird das der Einstiegs-App verwendet.

6.1. Java-Source zur (einzigen) Klasse 'JfxApp03'

package my.pkg;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;

import javafx.application.Application;
import javafx.event.Event;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.layout.Border;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class JfxApp03 extends Application {
    private static final File CONF_FILE = new File("./app-conf.properties"); // in project basedir
    private static final String CONF_WINPOS_X = "winPos_X";    // Property key
    private static final String CONF_WINPOS_Y = "winPos_Y";    // --''--
    private static final String DEFAULT_WINPOS_X_TXT = "60";   // Default Property value (text!!)
    private static final String DEFAULT_WINPOS_Y_TXT = "120";  // --''--

    private final Properties confProps = new Properties();
    private Stage primaryStage;
    private TabPane tabPane;
    private MenuBar menuBar;

    public static void main(String[] args) {
        launch(args);
    }

    public void init() throws Exception {
        super.init();
        System.out.println("init() - initialization (BEFORE access to GUI) called");
        loadConfig();
    }

    @Override
    public void stop() throws Exception {
        // Aufraeumen - MIT Zugriff auf GUI
        System.out.println("stop() called");
        storeConfig();
        super.stop();
    }

    @Override
    public void start(Stage primaryStage) {
        this.primaryStage = primaryStage;
        configWindow();

        BorderPane root = new BorderPane();

        Scene scene = new Scene(root /*, 400, 400*/);
        primaryStage.setScene(scene);

        // Stylesheet einbinden:
        String cssUrl = getClass().getResource("jfx-app-03.css").toExternalForm();
        scene.getStylesheets().add(cssUrl);
        System.out.format("CSS-File URL is '%s'%n", cssUrl);

        // MenuBar in Top-Bereich setzen:

        root.setTop(this.menuBar = composeMenuBar());

        // tabPane als BasisContainer des Center setzen:
        root.setCenter(this.tabPane = composeTabPane(2));  // Tab-Nr 1, 2 oder 3 aktivieren

        // Anzeige des Fensters:
        primaryStage.show();
    }

    public MenuBar composeMenuBar() {
        MenuBar menuBar = new MenuBar();
        Menu fileMenu = new Menu("Datei");
        Menu tabMenu = new Menu("Kein Tab");
        tabMenu.setId("tabMenu");
        Menu helpMenu = new Menu("Hilfe");
        menuBar.getMenus().addAll(fileMenu, tabMenu, helpMenu);
        MenuItem mniCreateFile = new MenuItem("Neue Datei");
        fileMenu.getItems().add(mniCreateFile);

        MenuItem mniAboutBox = new MenuItem("Über diese App ...");
        helpMenu.getItems().add(mniAboutBox);
        mniAboutBox.setOnAction(event -> {
            System.out.println("MenuItem 'aboutBox' aktiviert");
            Alert alert = new Alert(AlertType.INFORMATION);
            alert.setTitle("App-Info");
            alert.setHeaderText("Was ist diese App");
            alert.setContentText("Das ist die weitaus faszinierendste App");
            alert.initOwner(primaryStage);
            alert.showAndWait();
        });
        return menuBar;
    }

    public TabPane composeTabPane(int activeTabNr) {
        assert (activeTabNr >= 1 && activeTabNr <= 4)
                : "invalid activeTabNr (1..4): %d".formatted(activeTabNr);
        tabPane = new TabPane();

        // Tab 1: simpel, enthält nur 1 Label
        Tab tab1 = new Tab("Tab-1", new Label("Inhaltsbereich"));

        // Tab 2: tab2Vbox Referenz erzeugen mit tab2 auslesen und casten - (VBox)tab2.getContent():
        Tab tab2 = new Tab("Tab-2");
        tab2.setContent(new VBox());
        VBox tab2Vbox = (VBox) tab2.getContent();
        tab2Vbox.getChildren().add(new Label("Inhalt von Tab-2: Element A"));
        tab2Vbox.getChildren().add(new Label("Inhalt von Tab-2: Element B"));

        // Tab 3: tab3Vbox erst erzeugen, dann in TabPane aufnehmen:
        VBox tab3Vbox = new VBox();
        //tab3Vbox.setStyle("-fx-padding: 10 10 10 10;");
        tab3Vbox.getChildren().addAll( //
                new Label("Tab3-Label"), new TextField("Default-Text"), new Button("Button1"));
        Tab tab3 = new Tab("Tab-3", tab3Vbox);

        // Tab 4: enthält TextArea:
        Tab tab4 = new Tab("Tab-4", setupTextArea());
        // oben definierte Tabs der TabPane hinzufügen, tab mit 'activeTabNr' aktivieren:
        tabPane.getTabs().addAll(tab1, tab2, tab3, tab4);
        activateTab(activeTabNr);

//        TextField tf1 = new TextField();
//        tf1.setOn

        tab1.setOnSelectionChanged((Event event) -> {
            if (tab1.isSelected()) {
                System.out.println("Tab 1 wurde jetzt aktiviert");
                //TODO: Aktivieren des Datei-Laden/Speichern Menüs
            } else {
                //TODO: Disablen des Datei-Laden/Speichern Menüs
                System.out.println("Tab 1 ist jetzt nicht mehr selektiert");
            }
        });
        tab2.setOnSelectionChanged((Event event) -> {
            if (tab2.isSelected()) {
                System.out.println("Tab 2 wurde jetzt aktiviert");
            } else {
                System.out.println("Tab 2 ist jetzt nicht mehr selektiert");
            }
        });
        tab3.setOnSelectionChanged((Event event) -> {
            if (tab3.isSelected()) {
                System.out.println("Tab 3 wurde jetzt aktiviert");
            } else {
                System.out.println("Tab 3 ist jetzt nicht mehr selektiert");
            }
        });

        tab4.setOnSelectionChanged((Event event) -> {
            if (tab4.isSelected()) {
                System.out.println("Tab 4 wurde jetzt aktiviert");
            } else {
                System.out.println("Tab 4 ist jetzt nicht mehr selektiert");
            }
        });

        return tabPane;
    }

    public void activateTab(int tabNr) {  // tabNr 1, 2, 3, 4
        tabPane.getSelectionModel().select(tabNr - 1);  // tab mit tabNr aktiv machen
    }

    public TextArea setupTextArea() {  // in tab4 verwendet
        TextArea textArea = new TextArea();
        textArea.setEditable(true);
        textArea.setWrapText(true);
        return textArea;
    }

    public void configWindow() {
        assert (this.primaryStage != null) : "needed field 'primaryStage' not set yet";
        System.out.println("configWindow() called");
        // der Fenstertitel wird gesetzt (wie HTML-<title>):
        primaryStage.setTitle("Mein erstes JavaFX-Programm");
        // Das App-Icon wird gesetzt:
        String iconUrl = getClass().getResource("htlw5-logo1.png").toExternalForm();
        primaryStage.getIcons().add(new Image(iconUrl));
        // Die beim letzten Programmlauf gespeicherte Fenster-Position wird wieder verwendet:
        String winPosX_txt = confProps.getProperty(CONF_WINPOS_X, DEFAULT_WINPOS_X_TXT);
        primaryStage.setX(Double.parseDouble(winPosX_txt));
        String winPosY_txt = confProps.getProperty(CONF_WINPOS_Y, DEFAULT_WINPOS_Y_TXT);
        primaryStage.setY(Double.parseDouble(winPosY_txt));
    }

    public void loadConfig() {
        System.out.println("loadConfig() called");
        assert (confProps != null) : "no confProps set";  // compact way to check preconditions
        if (CONF_FILE.exists()) {
            try (FileReader fr = new FileReader(CONF_FILE)) {
                confProps.load(fr);
            } catch (IOException e) {
                System.out.format("Problem reading config file: %s", e.getMessage());
            }
        }
    }

    public void storeConfig() {
        System.out.println("storeConfig() called");
        assert (confProps != null) : "no confProps set";  // compact way to check preconditions
        // collecting all current settings within confProps:
        confProps.setProperty(CONF_WINPOS_X, "" + primaryStage.getX());
        confProps.setProperty(CONF_WINPOS_Y, "" + primaryStage.getY());
        //
        try (FileWriter fw = new FileWriter(CONF_FILE)) {
            confProps.store(fw, "JfxApp Config");
        } catch (IOException e) {
            System.out.format("Problem beim Schreiben von %s: %s", //
                    CONF_FILE.getAbsolutePath(), e.getMessage());
        }
    }

}

6.2. Dazugehöriges CSS-File 'jfx-app-03.css'

.root {      /*Basis-CSS-Klasse - dem root-Element der Scene zugeordnet*/
    -fx-font-size: 12pt;
    -fx-font-family: "Courier New";
/*     -fx-text-fill: blue; */
/*     -fx-background: rgb(32, 128, 128); */
}
#lblHello {
    -fx-font-family: Serif;
}

.lblHelloClass {
    -fx-font-weight: bold;
}

#btn2 {
    -fx-font-family: Helvetica, Arial, Sans-Serif;
}

/*
VBox {
    -fx-padding: 10 10 10 10;
    -fx-background: green;
}
*/
Tab  {
/*
    -fx-padding: 10 10 10 10;
    -fx-background: green;
*/

}

/* .tab-content-area > * */
.tab-content-area {
    -fx-padding: 10 10 10 10;
/*     -fx-background: green; */
}