edvin / tornadofx

Lightweight JavaFX Framework for Kotlin
Apache License 2.0
3.68k stars 272 forks source link

Implementing a split screen view for a workspace? #1252

Open SKeeneCode opened 4 years ago

SKeeneCode commented 4 years ago

Is it possible to implement a split screen view for a workspace? It would require the ability for the workspace to dock on either the left/right side, depending on which side has the user focus. This is with the workspace using tabs:

image

I've tried to think of a good way to do this, but to be honest I can't even come up with a bad way to do it. Any pointers? Or am I ultimately stuck on writing my own workspace implementation?

SchweinchenFuntik commented 4 years ago

https://docs.oracle.com/javase/8/javafx/api/javafx/scene/control/SplitPane.html

SKeeneCode commented 4 years ago

https://docs.oracle.com/javase/8/javafx/api/javafx/scene/control/SplitPane.html

Apologies I think I could have been more clear.

I meant when docking a view onto a workspace (or, clicking between tabs which does the same thing), instead of replacing the entire workspace with the docked view, only replace the left or right section, so I might have:

WorkSpace::dockInNewScopeLeftSide and WorkSpace::dockInNewScopeRightSide

And changing the user focus between the left and right side will load/deload the appropriate workspace buttons/drawers/drawer items etc associated with that view, and switch to the appropriate tab above.

The docs say:

Forwarding button state and actions As we have seen, the currently docked View controls the Workspace buttons. Some times you dock nested Views inside the main View, and you would like that nested View to control the buttons and actions instead. This can easily be done with the forwardWorkspaceActions function. You can change the forwarding however you see fit, for example on focus or on click on some component inside the nested View.

'Some times you dock nested Views inside the main View' That is what I suppose I want, The main view would be the split pane, and the nested views would be the left and right side. However I suspect this line is referring to simply an add() call considering the example directly below it.

SchweinchenFuntik commented 4 years ago

there is no such support.

This is not very difficult to do, but integrating with Workspace is already more difficult.

Any ideas for @edvin @ctadlock ?

SKeeneCode commented 4 years ago

Yeah looking through the workspace code I can't see a way to get what I want without writing a custom Workspace implementation, which I'll give a go and see what I can get at.

SKeeneCode commented 4 years ago

I had a go and realized that the way to implement it that I desired (a single line of tabs controlling two content containers) isn't the way most software actually does it. Intellij for example just adds in another tab pane next to the original that behaves seemingly completely independently from the other. Since the tab panes container seems nondetachable from the tab header without some dirty hacks I will drop the former idea.

oemergenc commented 4 years ago

TiwulFx has something like this already implemented, i am using it in some places in my TornadoFX Apps.

https://bitbucket.org/panemu/tiwulfx/src/master/

Detachable Pane Demo: https://www.youtube.com/watch?v=q_n23Ah1ftQ

(i am not the author)

SKeeneCode commented 4 years ago

Thanks @oemergenc I spent a few days understanding/modifying the dockable classes as well as rewriting a lot of TornadoFX's workspace class and got it working nicely:

image

oemergenc commented 4 years ago

Wow, very nice. Any chance to get this shared? 😃

SKeeneCode commented 4 years ago

Sure, although some things to note:

You can find the changes here: https://github.com/SKeeneCode/tornadofx2

oemergenc commented 4 years ago

Thanks for sharing. I played around with it and it seems to work very nice. Great work 👍🏻

Any chances to get this into the next snapshot version of tornado2? I think it is a very useable feature and a lot people would be happy to use it.

Maybe it would also be worth to integrate this in tornado1 as long as there is no final release of tornado2? In my application the TiwulFx implementation worked well with java 8.

I would also be happy to contribute. If possible, how?

Cheers

edvin commented 4 years ago

I'll be happy to merge a PR, provided it doesn't break any existing functionality :)

SKeeneCode commented 4 years ago

I would have to re-implement the Stack implementation. There is some opinionated decisions on what should happen when switching navigation modes (which is what I got stuck on and decided to just tear it all out).

If we don't worry about preserving view history/tabs apart from maybe the most recent then it would be relatively simple I think.

edvin commented 4 years ago

I don't think there is any need to track the tab view order. I think it's better to add them to the stack from left to right as they appear in the tab pane.

Docking the currently selected tab sounds like a good idea.

Switching from stack to tab could dock all the currently available tabs, that sounds like what a user might expect. This would keep some symmetry in going the other way as well.

The DetachableMode could either be introduced now, or better yet, introduced when/if the need arises.

SKeeneCode commented 4 years ago

Switching from stack to tab could dock all the currently available tabs

Can you clarify on what you mean by this?

edvin commented 4 years ago

Sorry, horrible formulation on my part :) If you switch from stack to tab, then all the entries in the stack should be converted to tabs IMO. The tab order should be the same as the stack order, and the view on top of the stack should be the active tab.

SKeeneCode commented 4 years ago

Understood, I'll have a crack at it over the next day or two.

SKeeneCode commented 4 years ago

So played around for 8 hours.. most of which is trying to fix a bug or two. One of which I've opened in issue over in tornadofx2.

I've hit a problem when it comes to docking the list of components in the viewStack.

If we take the existing workspace implementation and change the navigation change listener to this:

    private fun navigationModeChanged(oldMode: NavigationMode?, newMode: NavigationMode?) {
        if (oldMode == null || oldMode != newMode) {
            contentContainer = if (navigationMode == Stack) stackContainer else tabContainer
            root.center = contentContainer
            if (contentContainer == stackContainer && tabContainer.tabs.isNotEmpty()) {
                tabContainer.tabs.clear()
            }
            dockedComponent?.also {
                dockedComponentProperty.value = null
                dock(it, false) // this was changed from true to false
            }
        }
        if (newMode == Stack) {
            if (backButton !in root.header.items) {
                root.header.items.add(0, backButton)
                root.header.items.add(1, forwardButton)
            }
        } else {
            viewStack.toList().forEach { dock(it) } // this was added
            root.header.items -= backButton
            root.header.items -= forwardButton
        }
    }

The result is this:

image

As it docks each component that components dynamic components (in this case, the "p" button) are added and they are not removed as further components are added. I've tried undocking the component immediately after docking it. I've tried clearDynamicComponents everywhere I can think of and nothing seems to get rid of it.

The rest of basically implemented but I'm stuck on this problem.

If anyone has any ideas I'd love to learn :)