问题
My application allows users to use custom CSS themes to style the interface. I have several pre-built "themes" available to choose from that are very simple, with only 3 properties.
Sample CSS:
.root{
-fx-background: #325c81;
-fx-default-button: #77a3ca;
-fx-base: #a7c4dd;
}
The application has 3 ColorPicker
controls that need to allow users to select a color for each of those properties and save back to the CSS file.
I have no problem with actually writing the CSS file, but I cannot find a way to parse the .css
file in order to set the values of the ColorPicker
controls with the values from the .css
file.
Basic Program Flow
1) User selects a premade theme from ComboBox
:
cboPresetTheme.valueProperty().addListener((observable, priorTheme, newTheme) -> {
Utility.applyTheme(cboPresetTheme.getScene(), newTheme);
});
2) The associated .css
file is loaded and applied to the current Scene
:
public static void applyTheme(Scene scene, Theme theme) {
scene.getStylesheets().clear();
File css = new File("themes/" + theme.getFileName());
File fontFile = new File("themes/Font.css");
scene.getStylesheets().addAll(
css.toURI().toString(),
fontFile.toURI().toString());
}
3) The 3 ColorPicker
controls are updated with the values from the applied StyleSheet
:
cpBackground.setValue(Color.valueOf(cssFileBackground));
cpBase.setValue(Color.valueOf(cssFileBase));
cpDefaultButton.setValue(Color.valueOf(cssFileDefaultButton));
While I have no problem with steps 1 & 2, I do not know how to process step 3.
I have looked at other CSS Parser libraries (thank you, Google) but they seem more geared toward stand CSS and don't support FX properties. The StackExchange question edit or parse FX-CSS file programmatically appears to be asking the same question but it was never successfully answered.
One answer suggests using CSS Parser to accomplish this, but as there is little to know documentation (and what is there is beyond my current comprehension level), I don't know where to begin.
I understand there may not be a standard API currently available to accomplish this, but I was hoping there may be a simple library or solution out there that I have been unable to find.
回答1:
There are several ways you can tackle the conversion of a CSS declaration into a Color.
Style an auxiliar node
This is quite simple, but effective: The idea is that you could just style the background color of a node with the same css, and then set the colorPicker value with that color.
The only thing you need to take into account in this case is that the node is styled only when is added to a scene.
So you have to add the node to the scene. Adding a node with 0x0 size won't cause any issue, but maybe you don't want it to be there, so you can use an auxiliar scene.
public class CSSParsingApp extends Application {
@Override
public void start(Stage primaryStage) {
ColorPicker cpBackground = new ColorPicker(retrieveColor("value1"));
ColorPicker cpBase = new ColorPicker(retrieveColor("value2"));
ColorPicker cpDefaultButton = new ColorPicker(retrieveColor("value3"));
VBox root = new VBox(10, cpBackground, cpDefaultButton, cpBase);
root.setAlignment(Pos.CENTER);
Scene scene = new Scene(root, 300, 250);
scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
}
private Color retrieveColor(String value) {
Pane pane = new Pane();
pane.getStyleClass().add(value);
Scene sceneAux = new Scene(pane);
sceneAux.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
pane.applyCss();
return (Color) pane.getBackground().getFills().get(0).getFill();
}
public static void main(String[] args) {
launch(args);
}
}
where style.css
is:
.root {
-fx-background: #325c81;
-fx-default-button: #77a3ca;
-fx-base: #a7c4dd;
}
.value1 {
-fx-background-color: -fx-background;
}
.value2 {
-fx-background-color: -fx-default-button;
}
.value3 {
-fx-background-color: -fx-base;
}
Use StylableProperties
A similar, more elegant solution is found here. It uses StylableProperties
to create a node, that you can style with a custom -named-color
property, and then adds this helper
node to the main scene.
Basically it is the same idea as the one above, maybe more clean, as you don't need to modify your css file.
Using CssToColorHelper
, your code will be like this:
public class CSSParsingApp extends Application {
private CssToColorHelper helper = new CssToColorHelper();
@Override
public void start(Stage primaryStage) {
ColorPicker cpBackground = new ColorPicker();
ColorPicker cpBase = new ColorPicker();
ColorPicker cpDefaultButton = new ColorPicker();
VBox root = new VBox(10, cpBackground, cpDefaultButton, cpBase, helper);
root.setAlignment(Pos.CENTER);
Scene scene = new Scene(root, 300, 250);
scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
cpBackground.setValue(getNamedColor("-fx-background"));
cpDefaultButton.setValue(getNamedColor("-fx-default-button"));
cpBase.setValue(getNamedColor("-fx-base"));
primaryStage.setScene(scene);
primaryStage.show();
}
private Color getNamedColor(String name) {
helper.setStyle("-named-color: " + name + ";");
helper.applyCss();
return helper.getNamedColor();
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
where style.css
is your css file:
.root {
-fx-background: #325c81;
-fx-default-button: #77a3ca;
-fx-base: #a7c4dd;
}
Use JavaFX CSSParser
If you are looking for a CSSParser, why don't you just use the one included in JavaFX, the one you actually use to apply styling to your app?
It is under com.sun.javafx.css.parser.CSSParser
, and if the answer is you don't want to use private API, the good news is that it will be public API in JavaFX 9.
With it you can parse the css file and retrieve any parsed value easily.
public class CSSParsingApp extends Application {
@Override
public void start(Stage primaryStage) {
ColorPicker cpBackground = new ColorPicker();
ColorPicker cpBase = new ColorPicker();
ColorPicker cpDefaultButton = new ColorPicker();
VBox root = new VBox(10, cpBackground, cpDefaultButton, cpBase);
root.setAlignment(Pos.CENTER);
Scene scene = new Scene(root, 300, 250);
scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
cpBackground.setValue(parseColor("-fx-background"));
cpDefaultButton.setValue(parseColor("-fx-default-button"));
cpBase.setValue(parseColor("-fx-base"));
primaryStage.setScene(scene);
primaryStage.show();
}
private Color parseColor(String property) {
CSSParser parser = new CSSParser();
try {
Stylesheet css = parser.parse(getClass().getResource("style.css").toURI().toURL());
final Rule rootRule = css.getRules().get(0); // .root
return (Color) rootRule.getDeclarations().stream()
.filter(d -> d.getProperty().equals(property))
.findFirst()
.map(d -> ColorConverter.getInstance().convert(d.getParsedValue(), null))
.get();
} catch (URISyntaxException | IOException ex) { }
return Color.WHITE;
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
where style.css
is your css file:
.root {
-fx-background: #325c81;
-fx-default-button: #77a3ca;
-fx-base: #a7c4dd;
}
来源:https://stackoverflow.com/questions/42609756/how-to-parse-map-javafx-css-file-to-retrieve-its-properties-and-values