JavaFX handling Events on TableView


Question

In JaxaFX 8 I want to sort multiple columns of a TableView, which is possible via holding the shift-key while pressing onto the header.

But now it is supposed to run on a touch-enabled device, where I can't press the shift-key. I tried to catch Events on the TableView with addEventHandler(..), consume the original Event, create a new one with the modifier pressed and pass it back to the Control.

My approach in the constructor of extended TableView

final EventDispatcher orgDispatcher = getEventDispatcher();
    setEventDispatcher((event, tail) -> {
        if (event.getEventType() == MouseEvent.MOUSE_CLICKED) {
            final MouseEvent mouseEvent = (MouseEvent) event;
            final MouseEvent modifierEvent = new MouseEvent(MouseEvent.MOUSE_CLICKED, // type
                    mouseEvent.getX(), mouseEvent.getY(), mouseEvent.getScreenX(), mouseEvent.getScreenY(), // coords
                    MouseButton.SECONDARY, mouseEvent.getClickCount(), // clicks
                    true, true, true, true, // keyboard modifiers - shift-ctrl-alt-meta
                    false, false, false, // mouse modifiers
                    false, false, false, null);
            mouseEvent.consume();
            System.out.println("dispatched modified event" + mouseEvent.getButton() + modifierEvent.getButton());
            return orgDispatcher.dispatchEvent(modifierEvent, tail);
        }
        return orgDispatcher.dispatchEvent(event, tail);
    });

addEventHandler(MouseEvent.MOUSE_CLICKED, event -> {
        System.out.println(event.getButton());
    });

It seems that this approach works as clicking the TableView with primary prints secondary. But the sorting is not performed as expected. It seems that the ColumnHeader processes the event somewhere further down in the widget...

Can someone help in either a) modifiying the Event at the correct place or b) give an alternative solution to how to sort the columns and also adding this nice 'arrow'-icon to the column

The functionality is obviously already present I just don't know how to fire the event...

1
0
9/18/2014 10:21:51 AM

Accepted Answer

Apparently a solution to my problem is the following.

In order to programmatically set the sorting after multiple columns one has to make changes to the ObservableList<TableColumn<E,?>> getSortOrder() held by the TableView. One can simply add the TableColumn's in the correct order.

Since the Event causing this normally by shift-clicking is somewhere down in the JavaFX framework (probably in some Skin class or what?) I can't catch it via EventFilter. If I apply the filter on the TableView the magic has already happened...

Thus in my custom TableView I need to hold my own sortOrder ObservableList and replace it after each InputEvent. I haven't tested it on the touchenabled device, but without pressing Shift-key I can now sort after multiple columns. (doesn't really matter if MouseEvent or TouchEvent - the point being I don't have a keyboard to press the modifier key)

addEventFilter(MouseEvent.MOUSE_CLICKED, event -> {
        if (event.getTarget() instanceof TableColumnHeader) {
            event.consume();

            // Search for the clicked TableColumn:
            TableColumn<E, ?> column = null;
            final TableColumnHeader columHead = (TableColumnHeader) event.getTarget();
            final String name = columHead.getTableColumn().getText();
            for (final TableColumn<E, ?> col : getColumns()) {
                if (col.getText().equals(name)) {
                    column = col;
                }
            }

            // Now handle the sortOrder:
            if (sortedColumns.containsKey(name)) {
                if (sortedColumns.get(name) == TableColumn.SortType.ASCENDING) {
                    column.setSortType(TableColumn.SortType.DESCENDING);
                    sortedColumns.remove(name);
                    sortedColumns.put(name, TableColumn.SortType.DESCENDING);
                } else {
                    sortedColumns.remove(name);
                    sortOrder.remove(column);
                }
            } else {
                sortedColumns.put(name, TableColumn.SortType.ASCENDING);
                sortOrder.add(column);
            }

            // Override the getSortOrder():
            getSortOrder().clear();
            getSortOrder().addAll(sortOrder);
        }

    });

with fields

private final ObservableMap<String, TableColumn.SortType> sortedColumns = FXCollections.observableHashMap();
private final ObservableList<TableColumn<E, ?>> sortOrder = FXCollections.observableArrayList();
1
9/19/2014 9:28:07 AM

Note that consuming an event does not prevent other EventHandlers on TableView from being invoked (not to mention handlers registered on column headers directly, which are invoked prior to TableView's handlers). It only prevents event handlers on TableView's parents from being invoked.

You may want to add an event filter that consumes the event (which prevents any handlers from being invoked) and fires a modified event.

addEventFilter(MouseEvent.MOUSE_CLICKED, event -> {
    if(event.getButton() == MouseButton.PRIMARY) {
        MouseEvent modifierEvent = new MouseEvent(MouseEvent.MOUSE_CLICKED, // type
                event.getX(), event.getY(), event.getScreenX(), event.getScreenY(), // coords
                MouseButton.SECONDARY, event.getClickCount(), // clicks
                true, true, true, true, // keyboard modifiers - shift-ctrl-alt-meta
                false, false, false, // mouse modifiers
                false, false, false, null);
        event.consume(); // prevents any left-click handlers from being invoked
        Event.fireEvent(event.getTarget(), modifierEvent);
    }
});

Note that you don't need to replace the event dispatcher to fire an event. There is the Event.fireEvent() static method to do that.


Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon