DanielMcQ
DanielMcQ

Reputation: 37

fragment for flowpane in TornadoFX

The TornadoFX docs describe using the ListCellFragment to bind each cell in a list control to each item model in a list. I am wondering how to do something similar in a flowpane. I'd like to use that kind of class to render a bunch of controls and an SVG drawing in each cell. (So it would replace the button component in the example code below and somehow bind the shapeItem model to it).

class LibraryView : View("Current Library") {
    val shapeLibraryViewModel : LibraryViewModel by inject()
    override val root = anchorpane{
        flowpane {
            bindChildren(shapeLibraryViewModel.libraryItemsProperty){
               shapeItem -> button(shapeItem.nameProperty)
            }
        }
    }
}

Since I don't see a pre-made class like the one for list view, perhaps I would need to create something similar to it...or maybe there's a more lightweight approach?

Upvotes: 1

Views: 250

Answers (1)

Edvin Syse
Edvin Syse

Reputation: 7297

Using an ItemFragment is a bit overkill, since the itemProperty of the fragment will never change (a new fragment would be created for every item whenever the libraryItemsProperty change. However, if your view logic for each item is substantial, this approach will provide a clean way to separate and contain that logic, so it might be worth it. Here is a complete example you can use as a starting point.

class ShapeItemFragment : ItemFragment<ShapeItem>() {
    val shapeModel = ShapeItemModel().bindTo(this)

    override val root = stackpane {
        label(shapeModel.name)
    }
}

class ShapeItem(name: String) {
    val nameProperty = SimpleStringProperty(name)
}

class ShapeItemModel : ItemViewModel<ShapeItem>() {
    val name = bind(ShapeItem::nameProperty)
}

class LibraryViewModel : ViewModel() {
    val libraryItemsProperty = SimpleListProperty<ShapeItem>(
            listOf(
                    ShapeItem("Shape 1"),
                    ShapeItem("Shape 2")
            ).observable()
    )
}

class LibraryView : View("Current Library") {
    val shapeLibraryViewModel: LibraryViewModel by inject()

    override val root = anchorpane {
        flowpane {
            bindChildren(shapeLibraryViewModel.libraryItemsProperty) { shapeItem ->
                val itemFragment = find<ShapeItemFragment>()
                itemFragment.itemProperty.value = shapeItem
                itemFragment.root
            }
        }
    }
}

A slightly lighter version would be to pass the parameter into your fragment manually and just extend Fragment:

class ShapeItemFragment : Fragment() {
    val item: ShapeItem by param()

    override val root = stackpane {
        label(item.nameProperty)
    }
}

You can still bind to changes to properties inside the ShapeItem, since the underlying item won't change (as seen from the ItemFragment) anyway.

Your bindChildren statement would then look like this:

bindChildren(shapeLibraryViewModel.libraryItemsProperty) { shapeItem ->
    find<ShapeItemFragment>(ShapeItemFragment::item to shapeItem).root
}

Upvotes: 1

Related Questions