Skip to content
This repository has been archived by the owner on Sep 28, 2024. It is now read-only.

TableView column resizer extension function #48

Closed
thomasnield opened this issue Mar 11, 2016 · 7 comments
Closed

TableView column resizer extension function #48

thomasnield opened this issue Mar 11, 2016 · 7 comments

Comments

@thomasnield
Copy link
Collaborator

One thing that frustrated me for awhile is having a way to fit TableView columns to the header and cell contents. Then I found a little gem in a Stack Overflow post.

I was able to come up with this class to expose that resizeColumnToFitContent()

public final class TableColumnResizer<T> extends TableViewSkin<T> {

    public TableColumnResizer(TableView<T> tableView) {
        super(tableView);
    }

    @Override
    public void resizeColumnToFitContent(TableColumn<T, ?> tc, int maxRows) {
      super.resizeColumnToFitContent(tc,maxRows);
    }
}

Then I can apply that TableViewSkin to the skinProperty(), and call a resizeOp function at any time to re-fit the columns.

var resizeOp: (() -> Unit) by singleAssign()

//layout built here

tableView.apply {
    val resizerSkin = TableColumnResizer<FTRecord>(this);
    skinProperty().set(resizerSkin)
    resizeOp = { columns.forEach { resizerSkin.resizeColumnToFitContent(it, 100) } }
}

It would be awesome if we could apply a resizeColumns() extension function to the TableView. The problem is I can't think of an efficient way at the top of my head without extending TableView or doing some lazy operation that stores state elsewhere. It's really too bad extension properties can't be fields.

@edvin
Copy link
Owner

edvin commented Mar 25, 2016

We could always use reflection to call the method in the skin. It's not ideal, but it will work. If they ever remove the method, we can switch to another implementation but keep the interface. We can even choose different strategies in our extension function based on what's available on the skin. I'll give it a go :)

@edvin
Copy link
Owner

edvin commented Mar 25, 2016

I've come up with the following implementation. Can you check if it works for you?

fun <T> TableView<T>.resizeColumnsToFitContent(resizeColumns: List<TableColumn<T, *>> = columns, maxRows: Int = 20) {
    val resizer = skin.javaClass.getDeclaredMethod("resizeColumnToFitContent", TableColumn::class.java, Int::class.java)
    resizer.isAccessible = true
    resizeColumns.forEach { resizer.invoke(skin, it, maxRows) }
}

It supports optionally specifying only some columns, as well as the max number of rows to evaluate. Defaults are "all columns" and "20 rows".

I'm committing it so it's easier for you to test.

@thomasnield
Copy link
Collaborator Author

I like this idea a lot. However I am getting an error...

 val tbl = tableview<Person> {
                items = persons
                column("ID",Person::id)
                column("Name", Person::name)
                column("Birthday", Person::birthday)
                column("Age",Person::age)
            }
            tbl.resizeColumnsToFitContent()
java.lang.NullPointerException
    at tornadofx.MenuView.resizeColumnsToFitContent(MenuTest.kt:41)
    at tornadofx.MenuView.resizeColumnsToFitContent$default(MenuTest.kt:40)
    at tornadofx.MenuView.<init>(MenuTest.kt:36)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:422)
    at java.lang.Class.newInstance(Class.java:442)
    at tornadofx.FXKt.find(FX.kt:87)
    at tornadofx.App.start(App.kt:20)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$163(LauncherImpl.java:863)
    at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$176(PlatformImpl.java:326)
    at com.sun.javafx.application.PlatformImpl.lambda$null$174(PlatformImpl.java:295)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$175(PlatformImpl.java:294)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$149(WinApplication.java:191)
    at java.lang.Thread.run(Thread.java:745)

@edvin
Copy link
Owner

edvin commented Mar 25, 2016

That's probably because the skin isn't initialized yet. Try wrapping it in Platform.runLater { }. If that works, I can make sure it is automatically wrapped if the skin isn't available yet. Let me know!

@edvin
Copy link
Owner

edvin commented Mar 25, 2016

I commited a fix for this :)

@thomasnield
Copy link
Collaborator Author

Okay awesome, ill play with it again when I get back to my tablet.

@thomasnield
Copy link
Collaborator Author

Brilliant! Works perfectly. Thanks Edvin, that was pretty clever.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants