0
votes

I want to have a TreeView that has all of its children permanently expanded, and I don't want the user to be able to expand or collapse any of the children.

To do this I've found that I need to do the following:

Even though the icon is no longer visible, it's still clickable. I don't see any way of filtering this; I only see ways to be able to respond to it after the fact.

Also, if I'm missing anything else that I need to do to ensure this functionality, please let me know.

2
Do the tree items have any functionality at all (i.e. they can be clicked, with some custom action)?José Pereda
Yes; I implemented a custom TreeCell & set that as my cell factory. I'm not concerned with being able to start/cancel/commit edits, as my graphic for each TreeCell is a FlowPane with various ComboBoxes and other controls for the various underlying models that the cells are displaying. I still want them to be able to clicked, highlighted, and dragged though.Nathan
Is there possibly any way to remove the entire hit stage of the collapse button with CSS? I'd even settle with that at this point...Nathan
CSS just defines the view, but you want to alter the behavior. If you could provide your own skin implementation... TreeCellSkin extends TreeCellBehavior, which, at the end, is the class that handles all the clicking via handleClicks and handleDisclosureNode. But I'm not sure you want to go there...José Pereda

2 Answers

5
votes

I feel quite silly. I think this was mostly just a matter of not knowing what that darn arrow was called. Apparently it's a disclosureNode? Maybe that's common knowledge.

In the custom defined TreeCell, all I did was add this line in the updateItem method:

setDisclosureNode(null);
1
votes

The solution to avoid modifying the skin or the default behavior is more simple if we trap the clicks before they are dispatched, and consume the right ones.

For that we can use an EventDispatcher, to filter both the mouse pressed and the right click over the arrows, which are StackPane nodes:

class CellEventDispatcher implements EventDispatcher {

    private final EventDispatcher original;

    public CellEventDispatcher(EventDispatcher original) {
        this.original = original;
    }

    @Override
    public Event dispatchEvent(Event event, EventDispatchChain tail) {
        if (event.getEventType().equals(MouseEvent.MOUSE_PRESSED) || 
             event.getEventType().equals(ContextMenuEvent.ANY)){
            event.consume();
        }
        if(event instanceof KeyEvent && event.getEventType().equals(KeyEvent.KEY_PRESSED)){
            if((((KeyEvent)event).getCode().equals(KeyCode.LEFT) || 
                 ((KeyEvent)event).getCode().equals(KeyCode.RIGHT))){
                event.consume();
            }
        }
        return original.dispatchEvent(event, tail);
    }
}

Now we apply our custom dispatcher to the tree view:

@Override
public void start(Stage primaryStage) {
    TreeView<String> tree = new TreeView<>();
    ...
    EventDispatcher treeOriginal = tree.getEventDispatcher();
    tree.setEventDispatcher(new CellEventDispatcher(treeOriginal));

    Scene scene = new Scene(tree);
    primaryStage.setScene(scene);
    primaryStage.show(); 
}

This will consume any click (left or right) over the arrows on the tree.

EDIT

Added to the event dispatcher class the case where the user uses the keyboard to traverse the tree view, consuming the collapse/expand events with arrow LEFT or RIGHT.