I'm quite confused about the relationship between an ItemViewModel
and its underlying item. I'm trying to show a badge on the screen, and then if the badge is "achieved" on another thread, the badge should update on screen.
Right now, the badge code looks like this:
class Badge {
val achievedProperty = SimpleBooleanProperty(this, "achieved", false)
var achieved by achievedProperty
}
class BadgeModel: ItemViewModel<Badge>() {
val achieved = bind(Badge::achievedProperty)
}
Next, I want to display it on the screen, in an ItemFragment
subclass:
class BadgeFragment(badge: Badge): ItemFragment<Badge>() {
private val model: BadgeModel by inject()
init {
model.item = badge
}
override val root = rectangle {
width = 50
height = 50
stroke = Color.BLACK
fill = if (model.achieved) Color.RED else Color.GREEN
}
}
This works fine to start, but if I then set badge.achieved = true
(in my controller), the color of the badge on screen doesn't change.
I'm clearly missing something about the relationship between the object and the model, but I'm having a lot of trouble figuring it out from documentation. Can someone help me get my fragment working the way I want it to?
Your problem isn't specific to models or almost anything TornadoFX-related. The way you've written it only checks for the color once upon creation. You need to use properties or bindings to listen to property changes:
class Test : Fragment() {
val modelAchieved = SimpleBooleanProperty(true) // pretend this is model.achieved
val achievedColor = modelAchieved.objectBinding { isAchieved ->
if (isAchieved == true) Color.RED
else Color.GREEN
}
override val root = vbox {
togglebutton("Toggle") {
selectedProperty().bindBidirectional(modelAchieved)
}
rectangle(width = 50, height = 50) {
stroke = Color.BLACK
fillProperty().bind(achievedColor)
}
}
}
I would also bidirectionally bind the item properties of the viewmodel and fragment together so they stay in sync. Or just use a regular fragment, and reference the model's item property by itself.