0

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.

dan1st
  • 12,568
  • 8
  • 34
  • 67
Nathan
  • 162
  • 1
  • 9
  • Do the tree items have any functionality at all (i.e. they can be clicked, with some custom action)? – José Pereda Jan 07 '15 at 22:04
  • 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 Jan 07 '15 at 22:15
  • 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 Jan 07 '15 at 22:54
  • 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 Jan 07 '15 at 23:44

2 Answers2

5

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);
Nathan
  • 162
  • 1
  • 9
1

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.

José Pereda
  • 44,311
  • 7
  • 104
  • 132
  • Wouldn't this create a scenario where if the disclosure node (little arrow) where still there (although possibly invisible in my case), the user would click the invisible button, and the mouse click event would be consumed without the TreeItem being selected? – Nathan Jan 08 '15 at 00:42
  • Well, what you could do is just trigger the action for that treeItem (you'll have to find which item was clicked, though) – José Pereda Jan 08 '15 at 00:51
  • Just 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`. – José Pereda Jan 08 '15 at 01:01
  • ContextMenuEvent.ANY portion doesn't appear to work. I'm having it just print every event it gets & all I see it mouse events. I never see anything yield any ContextMenuEvents. The KeyEvent portion was very helpful though – Nathan Jan 08 '15 at 16:03
  • ContextMenuEvent happens when you right click to show a posible `ContextMenu`. In my simple snippet I could stop it. With the rest of your code maybe it's already disabled. – José Pereda Jan 08 '15 at 16:06
  • Oh, I see. I misunderstood. Right clicking -- Got it. I removed the "(event.getEventType().equals(MouseEvent.MOUSE_PRESSED) ||" portion though; This wasn't needed. Otherwise It's running quite nicely. Thanks! – Nathan Jan 08 '15 at 16:37
  • Ok, glad it works. I've just fixed some wrong formatting – José Pereda Jan 08 '15 at 16:41