ScalaFX: dynamic update of context menu of table rows
Context menus are useful to exhibit additional functionality. For my TLCockpit program I am listing the packages, updates, and available backups in a TreeTableView
. The context for each row should be different depending on the status of the content displayed.
My first try, taken from searches on the web, was to add the context menu via the rowFactory
of the TreeTableView
:
table.rowFactory = { p =>
val row = new TreeTableRow[SomeObject] {}
val infoMI = new MenuItem("Info") { onAction = /* use row.item.value */ }
val installMI = new MenuItem("Install") { onAction = /* use row.item.value */ }
val removeMI = new MenuItem("Remove") { onAction = /* use row.item.value */ }
val ctm = new ContextMenu(infoMI, installMI, removeMI)
row.contextMenu = ctm
row
}
This worked nicely until I tried to disable/enable some items based on the status of the displayed package:
...
val pkg: SomeObject = row.item.value
val isInstalled: Boolean = /* determine installation status of pkg */
val installMI = new MenuItem("Install") {
disable = isInstalled
onAction = /* use row.item.value */
}
What I did here is just pull the shown package, get its installation status, and disable the Install
context menu entry if it is already installed.
All good and fine I thought, but somehow reality was different. First there where NullPointerExceptions
(rare occurrence in Scala for me), and then somehow that didn’t work out at all.
The explanation is quite simple to be found by printing something in the rowFactory
function. There are only as many rows made as fit into the current screen size (plus a bit), and their content is dynamically updated when one scrolls. But the enable/disable status of the context menu entries were not properly updated.
To fix this one needs to add a callback on the displayed item, which is exposed in row.item
. So the correct code is (assuming that a SomeObject
has a BooleanProperty installed
):
table.rowFactory = { p =>
val row = new TreeTableRow[SomeObject] {}
val infoMI = new MenuItem("Info") { onAction = /* use row.item.value */ }
val installMI = new MenuItem("Install") { onAction = /* use row.item.value */ }
val removeMI = new MenuItem("Remove") { onAction = /* use row.item.value */ }
val ctm = new ContextMenu(infoMI, installMI, removeMI)
row.item.onChange { (_,_,newContent) =>
if (newContent != null) {
val isInstalled: /* determine installation status from newContent */
installMI.disable = is_installed
removeMI.disable = !is_installed
}
}
row.contextMenu = ctm
row
}
The final output then gives me:
That’s it, the context menus are now correctly adapted to the displayed content. If there is a simpler way, please let me know.
1 Response
[…] ScalaFX: dynamic update of context menu of table rows […]