2

I have some code, in javafx, that has a pane that works like a paint canvas. I need to be able to fill the background color of the pane from a color picker.

Currently I have a Color variable that gets the color chosen from the color picker and I try to set it to my Pane (named canvas) as below:

Color newColour = backgroundColourPicker.getValue();
canvas.setStyle("-fx-background-color: " + newColour + ";");

However I get this output:

June 11, 2022 7:47:57 PM javafx.css.CssParser term
WARNING: CSS Error parsing '*{-fx-background-color: 0x00ffffff;}: Unexpected token '0x' at [1,24]

How do I either swap my Color value to a String to be able to remove the leading 0x and make it work or how do I get my Pane to accept the color value as a Color?

Shenavyre
  • 61
  • 7

2 Answers2

3

I have found this code worked for me if anyone needs it in the future:

Color newColour = backgroundColourPicker.getValue();
Double red = newColour.getRed()*100;
int rInt = red.intValue();
Double green = newColour.getGreen()*100;
int gInt = green.intValue();
Double blue = newColour.getBlue()*100;
int bInt = blue.intValue();
String hex = String.format("#%02X%02X%02X", rInt, gInt, bInt);
canvas.setStyle("-fx-background-color: " + hex + ";");
Shenavyre
  • 61
  • 7
2

If you don't require all the features of the region's background—image, fills, etc.— and a solid color will suffice, also consider binding the color picker's value property to the fill color of a suitable Shape that fills the region. Based on the examples seen here, the variation below illustrates a ColorPane that renders a Rectangle whose size is bound to the enclosing Pane and whose color is bound to an ObjectProperty<Color> holding the rectangle's color. A similar arrangement is made for the foreground Circle. One benefit is that a custom color updates live as the the controls are adjusted. Several related examples are collected here.

two-tone

import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.InvalidationListener;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.NumberBinding;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.Scene;
import javafx.scene.control.ColorPicker;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

/**
 * @see https://stackoverflow.com/q/72583321/230513
 * @see https://stackoverflow.com/a/70312046/230513
 */
public class ColorTest extends Application {
    
    private final class ColorPane extends Pane {
        
        public static final Color bgColor = Color.BLUE;
        public static final Color fgColor = Color.CYAN;
        private final Rectangle r = new Rectangle();
        private final Circle c = new Circle(8, fgColor);
        private final ObjectProperty<Color> bg = new SimpleObjectProperty<>(bgColor);
        private final ObjectProperty<Color> fg = new SimpleObjectProperty<>(fgColor);
        private final InvalidationListener listener = (o) -> update();
        
        public ColorPane() {
            this.setPrefSize(256, 256);
            r.widthProperty().bind(this.widthProperty());
            r.heightProperty().bind(this.heightProperty());
            r.setFill(bgColor);
            this.getChildren().add(r);
            c.centerXProperty().bind(widthProperty().divide(2));
            c.centerYProperty().bind(heightProperty().divide(2));
            NumberBinding diameter = Bindings.min(widthProperty(), heightProperty());
            c.radiusProperty().bind(diameter.divide(2).subtract(diameter.divide(10)));
            this.getChildren().add(c);
            bg.addListener(listener);
            fg.addListener(listener);
        }
        
        private void update() {
            r.setFill(bg.get());
            c.setFill(fg.get());
        }
        
        public ObjectProperty<Color> bgProperty() {
            return bg;
        }
        
        public ObjectProperty<Color> fgProperty() {
            return fg;
        }
    }
    
    private Pane createControlPane(ColorPane view) {
        ColorPicker bgPicker = new ColorPicker(view.bgProperty().get());
        bgPicker.setTooltip(new Tooltip("Background color."));
        view.bgProperty().bindBidirectional(bgPicker.valueProperty());
        ColorPicker fgPicker = new ColorPicker(view.fgProperty().get());
        fgPicker.setTooltip(new Tooltip("Foreground color."));
        view.fgProperty().bindBidirectional(fgPicker.valueProperty());
        return new VBox(16, bgPicker, fgPicker);
    }
    
    @Override
    public void start(Stage stage) {
        var root = new BorderPane();
        ColorPane colorPane = new ColorPane();
        root.setCenter(colorPane);
        root.setLeft(createControlPane(colorPane));
        stage.setScene(new Scene(root));
        stage.show();
    }
    
    public static void main(String[] args) {
        launch(args);
    }
}

If you do require features of the region's background, the following example illustrates dynamically replacing the background of a GradientPane with one that contains a LinearGradient based on the chosen color properties. The approach has the same benefit of live custom color updates as the the controls are adjusted, illustrated above.

gradients

import javafx.application.Application;
import javafx.beans.InvalidationListener;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.NumberBinding;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.ColorPicker;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;

/**
 * @see https://stackoverflow.com/q/72583321/230513
 * @see https://stackoverflow.com/a/70312046/230513
 */
public class ColorTest extends Application {

    private final class GradientPane extends Pane {

        public static final Color c1Color = Color.BLUE;
        public static final Color c2Color = Color.CYAN;
        private final Circle c = new Circle(8, c2Color);
        private final ObjectProperty<Color> c1 = new SimpleObjectProperty<>(c1Color);
        private final ObjectProperty<Color> c2 = new SimpleObjectProperty<>(c2Color);
        private final InvalidationListener listener = (o) -> update();

        public GradientPane() {
            this.setPrefSize(256, 256);
            c.centerXProperty().bind(widthProperty().divide(2));
            c.centerYProperty().bind(heightProperty().divide(2));
            NumberBinding diameter = Bindings.min(widthProperty(), heightProperty());
            c.radiusProperty().bind(diameter.divide(2).subtract(diameter.divide(10)));
            this.getChildren().add(c);
            c1.addListener(listener);
            c2.addListener(listener);
            update();
        }

        private void update() {
            Stop[] stops = new Stop[]{new Stop(0, c1.get()), new Stop(1, c2.get())};
            LinearGradient lg = new LinearGradient(0.5, 0, 0.5, 1, true, CycleMethod.NO_CYCLE, stops);
            this.setBackground(new Background(new BackgroundFill(lg, CornerRadii.EMPTY, Insets.EMPTY)));
            stops = new Stop[]{new Stop(0, c2.get()), new Stop(1, c1.get())};
            lg = new LinearGradient(0.5, 0, 0.5, 1, true, CycleMethod.NO_CYCLE, stops);
            c.setFill(lg);
        }

        public ObjectProperty<Color> c1Property() {
            return c1;
        }

        public ObjectProperty<Color> c2Property() {
            return c2;
        }
    }

    private Pane createControlPane(GradientPane view) {
        ColorPicker bgPicker = new ColorPicker(view.c1Property().get());
        bgPicker.setTooltip(new Tooltip("Color stop one."));
        view.c1Property().bindBidirectional(bgPicker.valueProperty());
        ColorPicker fgPicker = new ColorPicker(view.c2Property().get());
        fgPicker.setTooltip(new Tooltip("Color stop two."));
        view.c2Property().bindBidirectional(fgPicker.valueProperty());
        VBox vBox = new VBox(10, bgPicker, fgPicker);
        vBox.setPadding(new Insets(10));
        return vBox;
    }

    @Override
    public void start(Stage stage) {
        var root = new BorderPane();
        GradientPane colorPane = new GradientPane();
        root.setCenter(colorPane);
        root.setLeft(createControlPane(colorPane));
        stage.setScene(new Scene(root));
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}
trashgod
  • 203,806
  • 29
  • 246
  • 1,045