I am using JavaFX ColorPicker in my application. As per my requirements, I have mapped the default colors on the color picker to a number. I want this number to be displayed as tooltip on hover over the color instead of hex value of the color. How can I achieve this?
//Part of Code
public void handleNodes(Circle circularNode) {
final Delta offset = new Delta();
circularNode.setOnMouseEntered(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
((Circle)(event.getSource())).setCursor(Cursor.HAND);
}
});
circularNode.setOnMousePressed(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
if(event.getButton().equals(MouseButton.SECONDARY)) {
System.out.println("Right click");
Circle parent = ((Circle)(event.getSource()));
final ContextMenu contextMenu = new ContextMenu();
MenuItem editLabel = new MenuItem("Edit Label");
editLabel.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
System.out.println("Edit Label");
final ColorPicker colorPicker = new ColorPicker();
colorPicker.setStyle("-fx-border-radius: 10 10 10 10;"
+ "-fx-background-radius: 10 10 10 10;");
colorPicker.setValue((Color) parent.getFill());
colorPicker.showingProperty().addListener((obs,b,b1)->{
if(b1){
PopupWindow popupWindow = getPopupWindow();
javafx.scene.Node popup = popupWindow.getScene().getRoot().getChildrenUnmodifiable().get(0);
popup.lookupAll(".color-rect").stream()
.forEach(rect->{
Color c = (Color)((Rectangle)rect).getFill();
Tooltip.install(rect.getParent(), new Tooltip("Custom tip for "+c.toString()));
});
}
});
panelMain.getChildren().add(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
ColorPickeris embedded in the main scene, as a regular node - The
ColorPickeris embedded in aContextMenu
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:
This is really a hacky answer.
The first problem: you have to find the popup node on the scene once it shows up. But you won't... since its not in the same window!
Having a deep look at how ScenicView does it, the trick is getting the list of windows at that moment, but using a deprectated method:
private PopupWindow getPopupWindow() {
@SuppressWarnings("deprecation") final Iterator<Window> windows = Window.impl_getWindows();
while (windows.hasNext()) {
final Window window = windows.next();
if (window instanceof PopupWindow) {
return (PopupWindow)window;
}
}
return null;
}
Once you have the popup window, we can now check for all the Rectangle nodes using lookupAll and the CSS selector color-rect, to get their color, and install the tooltip over its parent container:
@Override
public void start(Stage primaryStage) {
ColorPicker picker = new ColorPicker();
StackPane root = new StackPane(picker);
Scene scene = new Scene(root, 500, 400);
primaryStage.setScene(scene);
primaryStage.show();
picker.showingProperty().addListener((obs,b,b1)->{
if(b1){
PopupWindow popupWindow = getPopupWindow();
Node popup = popupWindow.getScene().getRoot().getChildrenUnmodifiable().get(0);
popup.lookupAll(".color-rect").stream()
.forEach(rect->{
Color c = (Color)((Rectangle)rect).getFill();
Tooltip.install(rect.getParent(), new Tooltip("Custom tip for "+c.toString()));
});
}
});
}
This is what it looks like:
来源:https://stackoverflow.com/questions/26954717/how-to-change-or-override-the-tooltip-in-javafx-colorpicker