Javafx Tooltip Customisation

Tooltip background (with JavaFX CSS)

The .root style class is added to the root node of all scenes. Since Tooltip has its own scene, tooltip's root node has the .root class as well, so the background image is applied to it as well, as per your CSS. The solution is to add a distinct style class to your main scene's root

scene.getRoot().getStyleClass().add("main-root");

and specify the background image for this distinct style class

.main-root {
-fx-background-image: url("media/background.jpg");
}

JavaFX quickfixes with tooltips and hyperlinks

You can add any node to a tooltip using the setGraphic() method. Here is a simple example demonstrating using a tooltip for "quick fix" functionality:

import java.util.Random;

import javafx.application.Application;
import javafx.css.PseudoClass;
import javafx.scene.Scene;
import javafx.scene.control.Hyperlink;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class TooltipWithQuickfix extends Application {

@Override
public void start(Stage primaryStage) {

TextField textField = new TextField();
textField.pseudoClassStateChanged(PseudoClass.getPseudoClass("invalid"), true);

textField.setTextFormatter(new TextFormatter<Integer>(c -> {
if (c.getText().matches("\\d*")) {
return c ;
}
return null ;
}));

textField.textProperty().isEmpty().addListener((obs, wasEmpty, isNowEmpty) ->
textField.pseudoClassStateChanged(PseudoClass.getPseudoClass("invalid"), isNowEmpty));

Tooltip quickFix = new Tooltip();
Hyperlink setToDefault = new Hyperlink("Set to default");
Hyperlink setToRandom = new Hyperlink("Set to random");
setToDefault.setOnAction(e -> {
textField.setText("42");
quickFix.hide();
});
Random rng = new Random();
setToRandom.setOnAction(e -> {
textField.setText(Integer.toString(rng.nextInt(100)));
quickFix.hide();
});

VBox quickFixContent = new VBox(new Label("Field cannot be empty"), setToDefault, setToRandom);
quickFixContent.setOnMouseExited(e -> quickFix.hide());
quickFix.setGraphic(quickFixContent);

textField.setOnMouseEntered(e -> {
if (textField.getText().isEmpty()) {
quickFix.show(textField, e.getScreenX(), e.getScreenY());
}
});

VBox root = new VBox(textField);

root.getStylesheets().add("style.css");

primaryStage.setScene(new Scene(root));
primaryStage.show();
}

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

with the stylesheet (style.css):

.root {
-fx-alignment: center ;
-fx-padding: 24 10 ;
}

.text-field:invalid {
-fx-control-inner-background: #ff7979 ;
-fx-focus-color: red ;
}

How to change or override the tooltip in JavaFX ColorPicker

Based on the code posted by the OP after my first answer, and due to the substancial changes in the problem addressed, I'm adding a new answer that covers both situations:

  • The ColorPicker is embedded in the main scene, as a regular node
  • The ColorPicker is embedded in a ContextMenu

In the second situation, the proposed solution for the first one is no longer valid, since the window found will be the one with the context menu.

A task is required to keep looking for windows until the one with the ComboBoxPopupControl is found.

This is a full runnable example:

public class ColorPickerFinder extends Application {

ExecutorService findWindowExecutor = createExecutor("FindWindow");

@Override
public void start(Stage primaryStage) {

AnchorPane panCircles = new AnchorPane();
Scene scene = new Scene(panCircles, 400, 400);

final Random random = new Random();
IntStream.range(0,5).boxed().forEach(i->{
final Circle circle= new Circle(20+random.nextInt(80),
Color.rgb(random.nextInt(255),random.nextInt(255),random.nextInt(255)));
circle.setTranslateX(100+random.nextInt(200));
circle.setTranslateY(100+random.nextInt(200));
panCircles.getChildren().add(circle);
});
panCircles.setPrefSize(400, 400);
ColorPicker colorPicker = new ColorPicker();

panCircles.getChildren().add(colorPicker);
primaryStage.setScene(scene);
primaryStage.show();

// We add listeners AFTER showing the stage, as we are looking for nodes
// by css selectors, these will be available only after the stage is shown
colorPicker.showingProperty().addListener((obs,b,b1)->{
if(b1){
// No need for task in this case
getPopupWindow();
}
});

panCircles.getChildren().stream()
.filter(c->c instanceof Circle)
.map(c->(Circle)c)
.forEach(circle->{
circle.setOnMouseClicked(e->{
if(e.getButton().equals(MouseButton.SECONDARY)){
// We need a task, since the first window found is the ContextMenu one
findWindowExecutor.execute(new WindowTask());

final ColorPicker picker = new ColorPicker();
picker.setStyle("-fx-border-radius: 10 10 10 10;"
+ "-fx-background-radius: 10 10 10 10;");
picker.setValue((Color)(circle.getFill()));
picker.valueProperty().addListener((obs,c0,c1)->circle.setFill(c1));

final ContextMenu contextMenu = new ContextMenu();
MenuItem editLabel = new MenuItem();
contextMenu.getItems().add(editLabel);
editLabel.setGraphic(picker);
contextMenu.show(panCircles,e.getScreenX(),e.getScreenY());
}
});
});

}

private PopupWindow getPopupWindow() {
@SuppressWarnings("deprecation")
final Iterator<Window> windows = Window.impl_getWindows();

while (windows.hasNext()) {
final Window window = windows.next();
if (window instanceof PopupWindow) {
if(window.getScene()!=null && window.getScene().getRoot()!=null){
Parent root = window.getScene().getRoot();
if(root.getChildrenUnmodifiable().size()>0){
Node popup = root.getChildrenUnmodifiable().get(0);
if(popup.lookup(".combo-box-popup")!=null){

// only process ComboBoxPopupControl
Platform.runLater(()->{
popup.lookupAll(".color-rect").stream()
.forEach(rect->{
Color c = (Color)((Rectangle)rect).getFill();
Tooltip.install(rect.getParent(),
new Tooltip("Custom tip for "+c.toString()));
});
});
return (PopupWindow)window;
}
}
}

return null;
}
}
return null;
}

private class WindowTask extends Task<Void> {

@Override
protected Void call() throws Exception {
boolean found=false;
while(!found){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
found=(getPopupWindow()!=null);
}
return null;
}

}

private ExecutorService createExecutor(final String name) {
ThreadFactory factory = r -> {
Thread t = new Thread(r);
t.setName(name);
t.setDaemon(true);
return t;
};
return Executors.newSingleThreadExecutor(factory);
}

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

}

This will be the result after right clicking on a circle, and clicking on the color picker:

Color Picker on ContextMenu

JavaFX styling TableColumn cells

You could use multiple class selectors to select the cells:

.column-1.table-cell {
-fx-background-color: white;
}
<TableColumn styleClass="column-1">


Related Topics



Leave a reply



Submit