Unfortunately, the table is not designed for the sorting to be changed in this way. However, I came up with a great solution to this problem after looking at the TableColumnHeader source code.
First you need to reverse the comparator on the column of interest:
Comparator<T> originalComparator = column.getComparator();
column.setComparator((v1, v2) -> originalComparator.compare(v2, v1));
This gets the sorting to be descending on first click and ascending on second click. The only problem is that the arrow in the column header hasn't changed, so will point up when it should point down and down when it should point up. To fix this, I added a style class to the column:
column.getStyleClass().add("descending-first-column");
and added this to my stylesheet:
.descending-first-column.column-header GridPane {
-fx-rotate: 180;
-fx-scale-x: -1;
}
.descending-first-column.column-header GridPane .sort-order {
-fx-scale-y: -1;
}
This works as follows. The rotation 180 degrees ensures the arrow points the right way. But when you sort multiple columns at a time the arrow will also have dots or a number (for columns 4 and above in the sort order). The GridPane contains the dots and the number as well as the arrow, so all of them get rotated 180 degrees. This works perfectly for the dots, but not for the number, which gets switched to the other side and turned upside down. So to put the number back to how it should be, I flipped the GridPane horizontally (-fx-scale-x: -1
) and flipped the number vertically (-fx-scale-y: -1
). And fortunately that just works without having to fiddle with translations.