JavaFX CSS und Vererbung - ich brauche nochmal etwas Nachhilfe

W

White_Fox

Top Contributor
Guten Abend allerseits

Ich habe in den letzten Tagen viel herumexperimentiert und gelesen, aber so richtig komme ich nicht weiter.

Folgendes: Ich will eine TreeView farblich hervorheben. Wenn ich in der CSS-Datei folgendes ändere:
Modena:
.tree-cell {
    -fx-background: red; /*-fx-control-inner-background;*/
    -fx-background-color: -fx-background;
    -fx-text-fill: -fx-text-background-color;
}
erscheint der betreffende Teil des Fensters rot (standard ist weiß) - alles ist gut.

Jetzt ergänze ich in der CSS-Datei folgendes:
CSS:
.tree-cell {
    -fx-background: -fx-control-inner-background;
    -fx-background-color: -fx-background;
    -fx-text-fill: -fx-text-background-color;
}

.tree-cell-jcls {
    -fx-background: red;
    -fx-background-color: -fx-background;
    -fx-text-fill: -fx-text-background-color;
}
Nun sieht das Fenster wieder so aus wie immer - so weit ist das auch ok. Jetzt gibt noch eine Methode, in der ich die Farbe ändern will (Zeile 61, 62):

Java:
public class LibraryBrowserPane extends Pane implements ModelObservable, VSlave {

    private ViewdataControllable mainview;

    private VBox vbox;
    private StackPane tree;
    private TreeView<String> treeView;
    private HashMap<TreeItem<String>, LibraryTreeitemModel> treeitemMap;
    private TreeItem<String> rootItem;
    private LibrarybrowserContextmenu treeContextmenu;

    Address getSelectedLibrarypage() {
        //...
    }

    public LibraryBrowserPane(ViewdataControllable mainview) {
        this.mainview = mainview;

        initGUI();
    }

    private void initGUI() {
        vbox = new VBox();
        vbox.setSpacing(10);
        getChildren().setAll(vbox);

        tree = new StackPane();
        vbox.getChildren().add(tree);

        refreshTreeview();
    }

    private void refreshTreeview() {
        //...
    }

    private void changeMarking() {
        //...
    }

    @Override
    public void notifyAboutChanges(ModelChange change) {
        //...
    }

    @Override
    public ArrayList<ModelChange> needModelregistration() {
        //...
    }

    void setContextMenu(LibrarybrowserContextmenu contextmenu) {
        //...
    }

    @Override
    public void receiveCommand(SlaveCommands command) {
        switch (command) {
            case CLEAR:
                break;
            case MARK_LIBRARYBROWSER:
                treeView.getStyleClass().clear();
                treeView.getStyleClass().add("tree-cell-jcls");
                break;
            default:
            //do nothing
        }
    }
}

Ich habe es auch mit allen anderen Nodeobjekten versucht, aber ohne Erfolg. Sieht da jemand, was ich nicht sehe oder was ich da verkehrt mache?
 
W

White_Fox

Top Contributor
Ja, das Aufrufen funktioniert einwandfrei.
 
Zuletzt bearbeitet:
dzim

dzim

Top Contributor
Hm... Du setzt den Style Class der TreeView neu (das #clear wird höchstwahrscheinlich nicht das .tree-view davon weg löschen). Aber du ersetzt nicht den Style Class der Zelle selbst.

Ich kann dir in dem Fall wirklich nur ScenicView empfehlen. Hat mir schon sehr oft geholfen!
 
W

White_Fox

Top Contributor
ScenicView krieg ich bei mir leider nicht zum Laufen, jedenfalls nicht für Java 8. Die für Java 15 läuft zwar, aber das hilft mir leider auch nicht weiter.

Wie kann ich denn die Style Class einer Zelle ändern? TreeItem stellt da keine Methoden zur Verfügung.
 
dzim

dzim

Top Contributor
Ich habe es in dem Fall temporär als dependency in meine Anwendung reingebracht und im onStageShown der primären Stage direkt gestartet.
Mit so einem Code-Block habe ich das Fenster von der Applikation aus gesteuert angezeigt.
Java:
primaryStage.setOnShown(event -> {
    try {
        Class<?> clazz = Class.forName("org.scenicview.ScenicView");
        if (clazz != null)
            clazz.getMethod("show", Scene.class).invoke(null, scene);
    } catch (Exception e) {
        LOG.trace("Sorry. There's no Scenic View, due to {} with message '{}'.", e.getClass().getName(), e.getMessage());
    }
});
Warum: Ich war zu faul, den Code jedes Mal zu löschen, so macht er den Scheiss immer, wenn die Klasse halt da ist, oder schreibt halt was ins Log. War mir egal... 😂

Hier die allgemeine Referenz zu CSS von Oracle (Java 8)
Mal am besten nach "Tree" suchen, das wäre hier jetzt zu viel alles zu erläutern. Es gibt auch was zur TreeCell - das ist schon mal der erste Ansatz.

Sehr viel helfen tut es, sich die modena.css anzuschauen. Die ist in der javafxrt.jar enthalten (J 8) oder sonst in einem der Module bei neueren OpenJFX-Releases (welches genau weiss ich nicht). Ich schau aber meistens hier https://gist.github.com/maxd/63691840fc372f22f470 - ja, outdated, aber so viel sollte sich da nicht getan haben. Auch hier könnte die Suche nach ".tree-cell" alles beschleunigen.
Wichtig ist, dass du den "Pfad" einer Klasse kennst. ".tree-view .tree-cell", etc.
 
W

White_Fox

Top Contributor
Tja...das Modena-CSS habe ich bereits in Verwendung und etwas modifiziert: Ich habe den Inhalt der .root-Klasse komplett kopiert und das '-fx'-Präfix verändert. Also in etwa so:

CSS:
/* Oringinal: */
.root{
    -fx-maincolor: #ececec;
    -fx-background: grey;
    ...
}

/* Überarbeitet: */
.root{
    -fx-maincolor: #ececec;
    -fx-background: grey;
    ...
   
    -jcls-hint-maincolor: #acacac;
    -jcls-hint-background: blue;
    ...
}

Anschließend habe ich sämtliche Styleklassen kopiert, die Styleklassennamen ebenfalls mit einem Präfix versehen und die Werte angepasst. Hier ist die Datei:
[Siehe nächster Kommentar]


Die Styleklassen für TreeView usw. hab ich schon gefunden. Aber wie gesagt - hat bisher nix gebracht. Und TreeItems bieten keine Unterstützung um die Styleklasse zu ändern.

Aber: Ich wäre auch zufrieden wenn ich einfach nur einen etwas dickeren Rahmen und die TreeView ziehen kann. Die TreeView befindet sich in einer StackPane, wenn ich den Rahmen um die StackPane ziehen kann wäre das auch ok. Allerdings hat das auch noch keine Erfolge gebracht. (In der JavaFX CSS Reverence ist ein Beispiel dafür, wie man so etwas bei einem Rechteck macht - das hab ich auch schon versucht unterzubringen, in verschiedenen Styleklassen, jedoch ohne Erfolg.)

Ich habe mal ein lauffähiges Minimalbeispiel, was ich aktuell mache. Den Inhalt oben in eine neue Textdatei kopieren und die Datei in modena.css umbenennen, und den Pfad zu dieser Datei in die Konstante 'csspath' eintragen, dann sollte es laufen.

Java:
package main.view;

import java.io.File;
import java.util.HashSet;
import javafx.scene.Scene;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;


public class TestCss extends javafx.application.Application {

    class LibraryBrowserPane extends StackPane {
        private TreeView<String> treeView;
        private HashSet<TreeItem<String>> treeitemMap;
        private TreeItem<String> rootItem;

        public LibraryBrowserPane() {
            initGUI();
        }

        private void initGUI() {
            refreshTreeview();
           
            getStyleClass().clear();
            getStyleClass().add("jcls-hint-tree-view");
            treeView.getStyleClass().clear();
            treeView.getStyleClass().add("jcls-hint-tree-view");
            treeView.applyCss();
            treeView.layout();
        }

        private void refreshTreeview() {
            rootItem = new TreeItem<>();

            //Create treeView items for view and link them with the according model treeView item
            treeitemMap = new HashSet<>();
            TreeItem parent = rootItem;
            for (int i = 0; i > 3; i++) {
                TreeItem<String> viewItem = new TreeItem<>(Integer.toString(i) + ". Item");
                treeitemMap.add(viewItem);
                parent.getChildren().add(viewItem);
                rootItem = viewItem;
            }

            treeView = new TreeView<>(rootItem);
            treeView.setShowRoot(false);
            getChildren().clear();
            getChildren().add(treeView);
        }
    }

    private final String csspath = ""; //Pfad zu "modena.css" setzen (Datei im Post)
    private BorderPane mainPane;

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

    @Override
    public void start(Stage primaryStage) throws Exception {
        File file;
        String cssURL;
        Scene mainScene;
        LibraryBrowserPane librarybrowser;

        //Load css file
        file = new File(csspath);
        cssURL = file.toURI().toURL().toExternalForm();

        //Generate main window
        mainPane = new BorderPane();
        mainScene = new Scene(mainPane, 1000, 600);
        mainScene.getStylesheets().clear();
        mainScene.getStylesheets().add(cssURL);
       
        librarybrowser = new LibraryBrowserPane();
        mainPane.setLeft(librarybrowser);
       
        primaryStage.setScene(mainScene);
        primaryStage.show();
    }
}
 
Zuletzt bearbeitet von einem Moderator:
W

White_Fox

Top Contributor
So, hier nochmal das ausführbare Minimalbeispiel und die zugehörige CSS-Datei als separate Dateien anstelle von Text im Post. Ach ja...die Dateiendungen noch entsprechend umbenennen (.java / .css), die Forensoftware akzeptiert diese Dateiformate nicht.
 

Anhänge

  • TestCss.txt
    2,2 KB · Aufrufe: 3
  • modena.txt
    262,1 KB · Aufrufe: 2
dzim

dzim

Top Contributor
Hat etwas gedauert... Erst Frage: Warum das ganze modena.css? Hast du das ganze Ding genommen und angefangen zu editieren? Das ist Overkill! Du brauchst nur ein CSS für deinen spezifischen Fall, nicht alles andere... ;)
Sonst ist es auch einfach zu unübersichtlich.

Also.
Ich habe mal folgendes CSS erstellt:
CSS:
/* standard cell */
.tree-cell {
    -fx-background: -fx-control-inner-background;
    -fx-background-color: -fx-background;
    -fx-text-fill: -fx-text-background-color;
}
/* Selected rows */
.tree-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-cell:filled:selected {
    -fx-background: -fx-selection-bar;
    -fx-table-cell-border-color: derive(-fx-selection-bar, 20%);
}
/* Selected when control is not focused */
.tree-cell:filled:selected {
    -fx-background: red; /* <-- this changes the default selection color */
    -fx-table-cell-border-color: derive(-fx-selection-bar-non-focused, 20%);
}
/*
Vorschlag
- Pseudo-Klasse erstellen
- TreeCell-Factory so anpassen, dass wenn dein Kriterium erfüllt ist, diese Pseudo-Klasse aktiviert wird
- https://guigarage.com/2016/02/javafx-and-css-pseudo-classes/

.tree-cell:filled:selected:jcls {
    -fx-background: jcls-farbe;
}
 */
/* focused cell (keyboard navigation) */
.tree-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-cell:focused {
    -fx-background-color: -fx-background, -fx-cell-focus-inner-border, -fx-background;
    -fx-background-insets: 0, 1, 2;
}

/*******************************************************************************
 *                                                                             *
 * TreeView and TreeCell                                                       *
 *                                                                             *
 ******************************************************************************/

.tree-cell {
    -fx-padding: 0.25em; /* 3 */
    -fx-indent: 1em;
}
.tree-cell .label {
    -fx-padding: 0.0em 0.0em 0.0em 0.25em; /* 0 0 0 3 */
}
.tree-cell > .tree-disclosure-node {
    -fx-padding: 4 6 4 8;
    -fx-background-color: transparent; /* <-- change the color of the arrows background */
}
.tree-cell > .tree-disclosure-node > .arrow {
    -fx-background-color: -fx-text-background-color; /* <-- change the color of the arrows */
    -fx-padding: 0.333333em 0.229em 0.333333em 0.229em; /* 4 */
    -fx-shape: "M 0 -3.5 L 4 0 L 0 3.5 z";
}
.tree-cell:expanded > .tree-disclosure-node > .arrow {
    -fx-rotate: 90;
}
Hab da die Farbe mal auf Rot geändert. (also eigentlich brauchst du dann nur den Teil mit .tree-cell:filled:selected

Wenn du das statt über Pseudo-Klasse über View machen möchstest, müsstes es eigentlich über .tree-view-jcls .tree-cell...... {} gehen. Also indem du die Style-Klassen von oben nimmst und ihnen nur ein .tree-view-jcls voran stellst (oder das .tree-view dagegen austauschst).
 
W

White_Fox

Top Contributor
Ich bin gerade auf Arbeit und kann mir das daher erst heute Abend zu Gemüte führen, dennoch schonmal sehr vielen Dank für deine Mühe. :)

Warum das ganze modena.css?
Ganz einfach: Einerseits habe ich damit den ganzen Vererbungsbaum der Elemente mit drin, was mir recht praktisch erschien. Z.B. sind dann auch Farbvariationen (Hauptfarbe 30% heller/dunkler, usw.) mit drin.

Außerdem will ich später noch ein paar andere Farbschemen einbauen (mindestens ein dunkles will ich haben, ich mag helle Farbschemen nicht so gerne), und so brauche ich später bloß ein paar Farben ändern, und in meinem Programm die Styleklassen drüberbügeln.
Ich will die Änderungen auch wieder rückgängig machen können, spätestens da brauche ich das ganze CSS wieder...oder nicht?
 
dzim

dzim

Top Contributor
Klar, man könnte das über eine Styleklasse machen, die irgendwo im root rangehängt wird. Würde es aber eher auf zwei CSS aufteilen und dann das CSS in der Scene oder so komplett austauschen. Und sachen aus modena, die ich nicht anpasse, kommen auch nicht ins eigene CSS, das ist – wie gesagt – Overkill.
Und die CSS-Verarbeitung ist (das musst jetzt mal ohne Quellen hinnehmen, bin zu faul sie rauszusuchen) eine der Ecken, in dem JavaFX schon irgendwie bescheiden ist... Also lieber so wenig wie möglich, um auch den eigenen Brain-Fuck im Rahmen zu halten. ;)
 
W

White_Fox

Top Contributor
Würde es aber eher auf zwei CSS aufteilen und dann das CSS in der Scene oder so komplett austauschen.
Ich hab schon überlegt ob ich das mit zwei CSS-Dateien erschlagen kann, aber ich wüßte nicht wie ich ein CSS auf ein einzelnes Nodeobjekt anwenden kann. Hast du vielleicht ein Minimalbeispiel von dem, was du meinst? (Oder reden wir aneinander vorbei?)


das musst jetzt mal ohne Quellen hinnehmen, bin zu faul sie rauszusuchen
Nein, das glaub ich dir auch so. :)
 
dzim

dzim

Top Contributor
So in etwa könnte man es angehen.

Java:
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setResources(StringResource.getResourceBundle());
fxmlLoader.setLocation(Main.class.getResource("ui/layout/Root.fxml"));
Pane root = fxmlLoader.load();
root.getStyleClass().add("main-root");
RootController controller = fxmlLoader.getController();
// so something with the controller

// Application.setUserAgentStylesheet(url); -> use, if there is a global CSS for EVERYTHING!
// e.g. if you want to replace modena.css

Scene scene = new Scene(root, windowSetup.getWidth(), windowSetup.getHeight());
scene.getStylesheets().add(getClass().getResource("res/common.css").toExternalForm());
scene.getStylesheets().add(getClass().getResource("res/specific-component.css").toExternalForm());
scene.getStylesheets().add(getClass().getResource("res/light.css").toExternalForm());
// ...

// e.g. Listener for some global application model property, that indicates a theme switch
ChangeListener<Boolean> themeChange = (obsetvable, n, o) -> {
    if (n == null || n = false) {
        // apply default theme, remove dark.css from scene.getStylesheets() and add light.css
    } else {
        // remove light.css from scene.getStylesheets() and add dark.css
    }
}
// add this listener to the property you need to listen to

primaryStage.setScene(scene);
 
W

White_Fox

Top Contributor
Und wie baue ich die CSS-Dateien dann auf? Einfach nur die Styleklassen unterschiedlich benennen?
 
dzim

dzim

Top Contributor
Nein. Identische Style-Klassen. Das geladene CSS entscheidet, wie dein Programm aussieht. Da du light und dark trennst und das jeweils andere entfernst, ist nur das aktiv, was du du brauchst.
 
dzim

dzim

Top Contributor
die wären in dem Beispiel statisch und immer drin. Da könnte so der generelle Stil, Grössen von Buttons, Padding, etc., drin stehen.
ist auch nur aufgeteilt, damit die einzelnen Files vielleicht nicht zu gross werden. Oder – wenn man mag – sogar dass man sie nur dann lädt, wenn man sie braucht. Ich hatte das mal für eine Leightweight-Dialog-API, die das CSS erst beim ersten Laden eines Dialogs angehängt hat.

Eigentlich umschalten muss man am Ende nur das, was sich zwischen Dark und Light unterscheidet. Wenn du es einfacher haben willst, kannst du auch dark und light in das Root-Pane reinladen (dann kannst du dort die Stylesheets einfach immer mit #clear() löschen, bevor du das neue lädst).
 
W

White_Fox

Top Contributor
Ich hätte jetzt (wenn ich das mit unterschiedlichen Farbschemen realisiere, für die Prealphaversion werde ich das noch nicht haben) nur ein einziges Stylesheet geladen. Mir geht es hier erstmal nur darum, Viewelemente hervorzuheben.

Und ich verstehe immer noch nicht, wie ich das mit zwei CSS-Dateien machen soll. Wie ich die Styleklasse ändere weiß ich ja halbwegs (außer bei dem Treeview-Hintergrund, da hat das nicht geklappt, aber z.B. bei Kontextmenüs geht das für einzelne MenuItems). Ich sehe aber immer noch nicht, wie ich denselben Effekt über zwei Stylesheets hinbekomme. Kannst du das noch näher erläutern?

Eine andere Idee, die ich noch hätte, wäre, zwar zwei Stile (einmal das eigentliche Farbschema sowie die Hervorhebung) in ein Stylesheet zu packen, aber insgesamt mit einem reduzierten Stylesheet zu arbeiten. Hier hat jemand mal ein - deutlich kleineres - Stylesheet eingestellt:

Das Stylesheet ist sehr viel kleiner als das originale Modena.css, aber es funktioniert. Zumindest habe ich es probehalber mal über mein Programm gezogen und es hat funktioniert. Ich habe allerdings die Befürchtung, daß da vieles fehlt. Modena.css wird nicht umsonst so riesig sein...oder?
 
dzim

dzim

Top Contributor
Naja. Wenn sie geladen sind, sind sie im Speicher, innerhalb der App, wie ein einziges CSS. Erst wenn du sie an spezifische Nodes hängst, kannst du komplett unterschiedliche Stile haben. Doch alles was von der Application (das interne modena.css) oder der Scene (Beispiel) kommt. Wenn ich mehr Zeit dafür hätte, würde ich dir ja ein Päckchen schnüren, aber im Moment ist das etwas schwierig.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
R JavaFX Java FXML Vererbung in Klassen AWT, Swing, JavaFX & SWT 9
O Ähnliche GUI Elemente - vererbung?! AWT, Swing, JavaFX & SWT 0
J Applet Applet Vererbung/Überladen AWT, Swing, JavaFX & SWT 5
S Button und Vererbung von diesem AWT, Swing, JavaFX & SWT 14
M Swing Vererbung, Probleme mit JDialog AWT, Swing, JavaFX & SWT 12
D Vererbung von Panels AWT, Swing, JavaFX & SWT 8
G vererbung mit swing? AWT, Swing, JavaFX & SWT 5
vogella Instanzisierung via Vererbung? AWT, Swing, JavaFX & SWT 2
J Fragen zur Vererbung und Update AWT, Swing, JavaFX & SWT 12
J 2D-Grafik Brauche Erklärung von Methode AWT, Swing, JavaFX & SWT 1
B Swing Wann brauche ich repaint() ? AWT, Swing, JavaFX & SWT 1
J Swing Wann brauche ich @Override vor der paint()-Methode? AWT, Swing, JavaFX & SWT 1
V Swing Brauche Hilfe mit Label AWT, Swing, JavaFX & SWT 3
K Swing Brauche Hilfe AWT, Swing, JavaFX & SWT 6
O AWT Performance und Bug behebung[brauche Hilfe] AWT, Swing, JavaFX & SWT 2
D Ambitioniertes Projekt - Brauche Stichworte AWT, Swing, JavaFX & SWT 4
D Swing Wozu brauche ich getContentPane()? AWT, Swing, JavaFX & SWT 2
S Ich brauche eine Idee: Animation mit teil eines Bildes AWT, Swing, JavaFX & SWT 16
L Mein Kopf dreht sich... Brauche Hilfe beim GUI gestalten AWT, Swing, JavaFX & SWT 10
J Brauche ganz dringend Hilfe!!! AWT, Swing, JavaFX & SWT 5
S Brauche genaustes Verstaendnis ueber JTextField AWT, Swing, JavaFX & SWT 3
M brauche Hilfe um Einträge aus Jlist zu löschen AWT, Swing, JavaFX & SWT 4
W Brauche Swing-Experten AWT, Swing, JavaFX & SWT 4
P Welchen Listener brauche ich? AWT, Swing, JavaFX & SWT 7
G Welche Swing-Version brauche ich . AWT, Swing, JavaFX & SWT 6
S Wann brauche ich die prepareRenderer() Methode ? AWT, Swing, JavaFX & SWT 2
D Brauche einfachen HTML Editor AWT, Swing, JavaFX & SWT 2
M Ich habe Werte und Brauche Grafik! AWT, Swing, JavaFX & SWT 3
G Leider trotz allem kein Icon :( Brauche Hilfe AWT, Swing, JavaFX & SWT 3
G Selbstprogrammierter Editor - brauche Hilfe AWT, Swing, JavaFX & SWT 5
M Brauche nur den Pfad zum Speichern AWT, Swing, JavaFX & SWT 8
M Brauche Hilfe zum Einbinden von eigener Schrfitart AWT, Swing, JavaFX & SWT 3
S Brauche Hilfe mit JFrame - NullPointerException AWT, Swing, JavaFX & SWT 7
K Ich brauche GridBagLayout Hilfe AWT, Swing, JavaFX & SWT 4
S Frame, brauche dringend hilfe AWT, Swing, JavaFX & SWT 4
M Konsolenprg-Ausgabe in GUI geleitet - Brauche ich Threads? AWT, Swing, JavaFX & SWT 2
N ProgressBar --> brauche Hilfe AWT, Swing, JavaFX & SWT 4
N Brauche Layout-Hilfe AWT, Swing, JavaFX & SWT 3
N Brauche Layout Hilfe AWT, Swing, JavaFX & SWT 9
Z Bei Problem mit Java Swing brauche dringende Hilfe AWT, Swing, JavaFX & SWT 3
O Brauche ich einen neuen Frame? AWT, Swing, JavaFX & SWT 2
V Brauche Hilfe beim Steuern des Sichtbereiches eines JPanel AWT, Swing, JavaFX & SWT 2
D Zoom problem!!! (brauche screenshot...) plz help thx AWT, Swing, JavaFX & SWT 7
M Nochmal Swing, MVC und generelles Design AWT, Swing, JavaFX & SWT 2
GilbertGrape nochmal editierbare Combobox AWT, Swing, JavaFX & SWT 3
D Nochmal ne Frage zum DefaultTableModel AWT, Swing, JavaFX & SWT 8
J Nochmal Drag&Drop, ABER: Object verschieben AWT, Swing, JavaFX & SWT 2
S Nochmal Spaltenbriete im Table AWT, Swing, JavaFX & SWT 2
S Nochmal GUI mit JMF Player AWT, Swing, JavaFX & SWT 13
G Nochmal Threads und Einfrieren des GUI :( AWT, Swing, JavaFX & SWT 10

Ähnliche Java Themen

Anzeige

Neue Themen


Oben