Search code examples
javacheckboxjavafxtreeviewantialiasing

JavaFX CheckBoxTree in popup of drop-down Button


In order for the end-user to constrain a search to some columns of the main TableView, I needed a treeview with checkboxes. I decided to embed this TreeView in a popup, showing on click on a custom button.

I have created the following class, inspired from the question: Java FX8 TreeView in a table cell

public class CustomTreeMenuButton extends MenuButton {
    private PopupControl popup = new PopupControl();
    private TreeView<? extends Object> tree;
    private CustomTreeMenuButton me = this;

    public void setTree(TreeView<? extends Object> tree) {
        this.tree = tree;
    }

    public CustomTreeMenuButton() {
        super();
        this.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {

            @Override
            public void handle(MouseEvent event) {
                if (!popup.isShowing()) {

                    Bounds b = me.localToScreen(me.getBoundsInLocal());
                    double x = b.getMinX();
                    double y = b.getMaxY();

                    popup.setAutoHide(true);
                    // popup.setAutoFix(true);
                    popup.setAnchorX(x);
                    popup.setAnchorY(y);

                    popup.setSkin(new Skin<Skinnable>() {
                        @Override
                        public void dispose() {
                        }

                        @Override
                        public Node getNode() {
                            return tree;
                        }

                        @Override
                        public Skinnable getSkinnable() {
                            return null;
                        }
                    });

                    popup.show(me.getScene().getWindow());
                }
            }
        });
    }
}

The tree I am working with contains CheckBoxTreeItem objects, and while the popup is working, there is some weird blur on all checkboxes, whenever the focus is not on a checkbox. (See GIF below)

CheckBoxTreeItem Blur

First, I was thinking it was maybe an antialiasing problem, but popup.getScene().getAntiAliasing().toString() returns DISABLED

Then, I saw that non integer anchor points could cause problems. However popup.setAutoFix(true) did nothing, nor did the following:

popup.setAnchorX(new Double(x).intValue());
popup.setAnchorY(new Double(y).intValue());

It might be worth noting that I am working with FXML.

How can I get sharp checkboxes regardless of their focus ?


Solution

  • I would suggest a built-in control, CustomMenuItem, rather than reinventing the wheel:

    A MenuItem that allows for arbitrary nodes to be embedded within it, by assigning a Node to the content property.

    An example

    // Create the tree
    CheckBoxTreeItem<String> rootItem = new CheckBoxTreeItem<String>("All stuff");
    rootItem.setExpanded(true);                  
    
    final TreeView<String> tree = new TreeView<String>(rootItem);  
    tree.setEditable(true);
    
    tree.setCellFactory(CheckBoxTreeCell.<String>forTreeView());    
    for (int i = 0; i < 8; i++) {
        final CheckBoxTreeItem<String> checkBoxTreeItem = 
                new CheckBoxTreeItem<String>("Stuff" + (i+1));
        rootItem.getChildren().add(checkBoxTreeItem);                
    }
    
    tree.setRoot(rootItem);
    tree.setShowRoot(true);
    
    // Create a custom menu item
    CustomMenuItem customMenuItem = new CustomMenuItem(tree);
    customMenuItem.setHideOnClick(false);
    
    // Create the menu button
    MenuButton mb = new MenuButton("Stuffs");
    mb.getItems().add(customMenuItem);
    

    And the output

    enter image description here

    Note: It is important to set the hideOnClickProperty to true, to avoid closing when the user clicks in the tree, which can be even done in the contructor, so you can shorten the initialization to:

    CustomMenuItem customMenuItem = new CustomMenuItem(tree, false);
    

    If you want to remove the hover glow, you can add the following CSS class:

    .menu-item {
        -fx-padding: 0;
    }