TornadoFX JavaFX Sync Desplazarse por las vistas de tabla

Estoy intentando sincronizar pergaminos en las vistas de tabla. (Horizontal y Vertical)

enter image description here

La Vista SyncScrollEx tiene dos tableView que es básicamente un Fragmento colocado uno al lado del otro, con el mismo set de datos y, por lo tanto, el mismo layout de tamaño de tabla.

Comportamiento esperado: cuando me desploop en una vista de tabla, la barra de desplazamiento de la otra tabla también debe desplazarse por la misma cantidad.

Debajo está mi progreso actual:

import javafx.beans.property.SimpleIntegerProperty import javafx.beans.property.SimpleStringProperty import javafx.collections.FXCollections import javafx.scene.control.ScrollBar import tornadofx.* class SyncScrollEx : View() { override val root = hbox { setPrefSize(300.0, 150.0) this += find<MyTableFrag>() this += find<MyTableFrag>() } } class MyTableFrag : Fragment() { var addEventOnlyOnceFlag = false val persons = FXCollections.observableArrayList<GameWarrior>( GameWarrior(1,"Tyrion Lannister", "M"), GameWarrior(2,"Ned Stark", "M"), GameWarrior(3,"Sansa Stark", "F"), GameWarrior(4,"Daenerys Targaryen", "F"), GameWarrior(5,"Bran Stark", "M"), GameWarrior(6,"Jon Snow", "M"), GameWarrior(7,"Arya Stark", "F") ) override val root = vbox { tableview(persons) { column("ID", GameWarrior::idProperty) column("Name", GameWarrior::nameProperty) column("Gender", GameWarrior::genderProperty) subscribe<SyncScrollEvent> { event -> //Sync the ScrollX & ScrollY of both the tables event.node.value = event.newVal.toDouble() } //Hack, need to initialize this when the table/scroll is rendenetworking setOnMouseEntenetworking { //Hack for not triggering the lookupAll event on every mouse enter if (!addEventOnlyOnceFlag) { addEventOnlyOnceFlag = true //INFO: Look up for the scroll bars in tableView and add a listener this.lookupAll(".scroll-bar").map { node -> if (node is ScrollBar) { node.valueProperty().addListener { value, oldValue, newValue -> println(node.orientation.toString() + " " + newValue) fire(SyncScrollEvent(node, newValue)) } } } } } } } } class GameWarrior(id: Int, name: String, gender: String) { val idProperty = SimpleIntegerProperty(id) var id by idProperty val nameProperty = SimpleStringProperty(name) var name by nameProperty val genderProperty = SimpleStringProperty(gender) var gender by genderProperty } class SyncScrollEvent(val node: ScrollBar, val newVal: Number) : FXEvent() 

Los comentarios resaltan los problemas que estoy enfrentando.
Además, no entiendo cómo se invocará el "suscribir" para las dos tablas en dicho escenario donde Fire () ocurre dentro de un EventListener.

Primero, necesitamos acceso limpio a las barras de desplazamiento. Cuando TableView tiene asignado su skin, las barras de desplazamiento estarán disponibles. Crearemos un map con una orientación para seguirlos:

 val scrollbars = HashMap<Orientation, ScrollBar>() 

Una vez que la máscara está disponible, buscamos las barras de desplazamiento y las asignamos a nuestro map y escuchamos los cambios para poder activar el evento.

 skinProperty().onChange { this.lookupAll(".scroll-bar").map { it as ScrollBar }.forEach { bar -> scrollbars[bar.orientation] = bar bar.valueProperty().onChange { fire(SyncScrollEvent(bar, this)) } } } 

No necesitamos la position en el evento, ya que podemos consultar la barra de desplazamiento para get su valor, pero es más fácil filtrar los events si agregamos el TableView de origen. SyncScrollEvent ahora se ve así:

 class SyncScrollEvent(val scrollbar: ScrollBar, val table: TableView<*>) : FXEvent() 

Escuchemos los events de desplazamiento y asegúrese de que solo cambiemos nuestro valor de la barra de desplazamiento si el evento se origina en la otra vista de tabla, para la orientación correspondiente:

 subscribe<SyncScrollEvent> { event -> if (event.table != this) scrollbars[event.scrollbar.orientation]?.value = event.scrollbar.value } 

Para completar, aquí está toda la aplicación modificada:

 import javafx.beans.property.SimpleIntegerProperty import javafx.beans.property.SimpleStringProperty import javafx.collections.FXCollections import javafx.geometry.Orientation import javafx.scene.control.ScrollBar import javafx.scene.control.TableView import tornadofx.* import java.util.* class SyncScrollEx : View() { override val root = hbox { setPrefSize(300.0, 150.0) add(MyTableFrag::class) add(MyTableFrag::class) } } class MyTableFrag : Fragment() { val persons = FXCollections.observableArrayList<GameWarrior>( GameWarrior(1, "Tyrion Lannister", "M"), GameWarrior(2, "Ned Stark", "M"), GameWarrior(3, "Sansa Stark", "F"), GameWarrior(4, "Daenerys Targaryen", "F"), GameWarrior(5, "Bran Stark", "M"), GameWarrior(6, "Jon Snow", "M"), GameWarrior(7, "Arya Stark", "F") ) val scrollbars = HashMap<Orientation, ScrollBar>() override val root = vbox { tableview(persons) { column("ID", GameWarrior::idProperty) column("Name", GameWarrior::nameProperty) column("Gender", GameWarrior::genderProperty) subscribe<SyncScrollEvent> { event -> if (event.table != this) scrollbars[event.scrollbar.orientation]?.value = event.scrollbar.value } skinProperty().onChange { this.lookupAll(".scroll-bar").map { it as ScrollBar }.forEach { bar -> scrollbars[bar.orientation] = bar bar.valueProperty().onChange { fire(SyncScrollEvent(bar, this)) } } } } } } class GameWarrior(id: Int, name: String, gender: String) { val idProperty = SimpleIntegerProperty(id) var id by idProperty val nameProperty = SimpleStringProperty(name) var name by nameProperty val genderProperty = SimpleStringProperty(gender) var gender by genderProperty } class SyncScrollEvent(val scrollbar: ScrollBar, val table: TableView<*>) : FXEvent() 
  • ¿Cuál es la mejor práctica para crear un componente de IU personalizado en tornadofx?
  • ¿Cómo llenar el ancho de la tabla disponible con columnas?
  • Dibujando líneas desde ViewModel TornadoFX
  • tornadoFX togglebutton no tiene propiedad de text
  • java.lang.ClassNotFoundException: no encontró la class "com.my.app.example.Main" en la ruta: DexPathList
  • Enlace de un model de dominio con campos anulables en ItemViewModel
  • TornadoFX: permite copyr un elemento de una vista de list al portapapeles
  • Context de reinicio de JavaFX
  • Enlazando una vista a una propiedad de controller cambiante
  • El elemento TornadoFx ItemViewModel es nulo
  • TornadoFX - eliminar elemento con ContextMenu hacer clic derecho