I am trying to synchronise scrolls across the tableviews. (Both Horizontal & Vertical)
The SyncScrollEx View has two tableView which is basically one Fragment placed side by side, with same dataset, and hence same table size layout.
Expected Behaviour: When I scroll on one tableview, the other tableview's scrollbar should also scroll for the same amount.
Below is my current progress:
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 rendered
setOnMouseEntered {
//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()
The comments highlight the problems I am facing.
Also, I fail to understand how the "subscribe" will get invoked for both the tableviews in such scenario where Fire() happens inside a EventListener
First we need clean access to the scrollbars. When the TableView is assigned it's skin, the scrollbars will be available. We'll create a map keyed on orientation to keep track of them:
Once the skin is available we look up the scrollbars and assign them to our map and listen for changes so we can fire the event
We don't need the position in the event, since we can query the scrollbar for it's value, but it's easier to filter out the events if we add the source TableView. The SyncScrollEvent now looks like this:
Let's listen for the scroll events and make sure we only change our scrollbar value if the event originates from the other tableview, for the corresponding orientation:
For completeness, here is the whole modified app: