How to change or override the tooltip in JavaFX ColorPicker

后端 未结 2 1817
孤街浪徒
孤街浪徒 2020-12-22 12:27

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 displaye

相关标签:
2条回答
  • 2020-12-22 13:21

    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:

    Color picker with custom tooltip

    0 讨论(0)
  • 2020-12-22 13:26

    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

    0 讨论(0)
提交回复
热议问题