B4J Library Dockable windows

I haven't been doing much programming for a while, but I have a small project to do in B4j and wanted a dockable interface. I came across this https://github.com/RobertBColton/DockFX which appears to do what I want.

To test it I recreated the example app (java code here : https://github.com/RobertBColton/DockFX/blob/master/src/main/java/org/dockfx/demo/DockFX.java)

The B4j project is attached. The library hasn't been updated for a while, but I think I can use it as is, so I thought I would share it in case anybody else wanted to.

Download the zipped jar file (dockfx-0.1b.zip) from here : https://github.com/RobertBColton/DockFX/releases, unzip it and copy to your additional libraries folder.

For documentation, I'm afraid you'll have to read the java code available on github.

It's LGPL3 licensed, make sure it's appropriate for your project.

Requires JavaObject and Reflection libraries, see the Test app code for explanations.

06 Jan 2017 : Added jDockFXCV - Custom View version see Post7

06 Jan 2017 : Added more methods, including return underlying Node as Pane (if it is one) to load layouts on. Pass Null to the Initialization and a Pane will be created in the Class.

jDockFX


Library: jDockFX

Author: Steve Laming
Version: 0.03

Depends on:
javaobject, jreflection, dockfx-0.1b

Class: DockPane

  • Class Description:
    • The parent on which DockNodes can be Docked[/I]
  • Methods:
    • AsJO As JavaObject
      • Get the underlying object as a JavaObject
    • AsNode As Node
      • Get the underlying object as a Node
    • DesignerCreateView(Base As Pane , Lbl As Label , Props As Map ) As String
      • Called from the designer on creation
    • GetBase As Pane
      • Get the BasePane for this CustomView
    • Initialize(vCallback As Object , vEventName As String ) As String
      • Called from the designer on initialization
    • IsInitialized As Boolean
      • Tests whether the object has been initialized.
    • NodeCount As Int
      • Returns the number of DockNodes attached to this DockPane
    • SetDefaultUserAgentStylesheet As String
      • Initialize the default styles For the dock pane And undocked nodes using the DockFX
        library's internal Default.css stylesheet
        unlike other custom control libraries this allows the user To override them globally
        using the style manager just As they can with internal JavaFX controls
        this must be called after the primary stage Is shown
        https://bugs.openjdk.java.net/browse/JDK-8132900
    • SplitPane As SplitPane
      • Gets the underlying SplitPane for this DockPane

jDockFX


Library: jDockFX

Author: Steve Laming
Version: 0.03

Depends on:
javaobject, jreflection, dockfx-0.1b

Class: DockNode

  • Class Description:
    • DockNode class represents a dockable Node which can be added to a DockPane[/I]
  • Properties:
    • Closable(Value As Boolean) As Boolean
      • Get / Set the closable property for this DockNode
    • Contents(Value As Node) As Node
      • Get / Set (replace) the current Content Node
    • DockTitleBar(Value As Node) As Node
      • Get/Set the titlebar for this Dock, set it to Null to disable docking and undocking
    • Pane As Pane - [Read Only]
      • Returns the underlying Node as a Pane (if it is one Null if not). You can add nodes or load a layout to this.
    • Tag(Value As Object) As Object
      • Get / Set the Tag Object for this DockNode
  • Methods:
    • Dock(Parent As DockPane , Location As String ) As DockNode
      • Dock this node to a parent.
        Parent a DockPane
        Location one of "CENTER", "TOP", "BOTTOM", "LEFT" or "RIGHT"
    • Graphic(FilePath As String , FileName As String ) As DockNode
      • Set a Graphic for this DockNode. Returns this DockNode
    • Initialize(Module As Object , EventName As String , Title As String , Node As Node ) As DockNode
      • Initializes the object.
        Module - for callback
        EventName - for callback
        Title - the title for the DockNode
        Node - the content of this DockNode, Pass Null if you want to load a layout later
        Returns this DockNode
    • IsDocked As Boolean
      • Get the current Docked Status of this DockNode
    • IsInitialized As Boolean
      • Tests whether the object has been initialized.
    • PrefSize(Width As Double , Height As Double ) As DockNode
      • Set the pref size for this DockNode. Returns this DockNode
    • RemoveDockTitleBar(Archive As Boolean ) As DockNode
      • Remove the DockTitleBar (Disables undocking / Docking) and optionally archive it so we can restore it later
    • RestoreDockTitleBar As DockNode
      • Restore an archived DockTitleBar
 

Attachments

  • DockFXTest.zip
    3.9 KB · Views: 638
  • DockFxDemo.png
    DockFxDemo.png
    28.8 KB · Views: 1,118
  • jDockFXCV0.3.zip
    8.1 KB · Views: 672
Last edited:

jmon

Well-Known Member
Licensed User
Longtime User

stevel05

Expert
Licensed User
Longtime User
The beauty of this one is that I didn't need to port anything. It is already distributed as a jar file that is accessed using JavaObject. Just needed a little time to work out how to use it.
 

stevel05

Expert
Licensed User
Longtime User
If you want to know when a window is docked or undocked you can use this code:
B4X:
Sub DockNodeSetEventHandler(Node As JavaObject,EventName As String)
   
    Dim R As Reflector
    R.Target = Node
    Dim DP As JavaObject =  R.GetField("dockedProperty")
    Dim Event As Object = DP.CreateEvent("javafx.beans.value.ChangeListener",EventName & "_DockChanged",True)
    DP.RunMethod("addListener",Array(Event))
   
End Sub

Sub TD_DockChanged_Event(MethodName As String,Args() As Object) As Object
    'Get the property value
    Dim Docked As JavaObject = Args(0)
    Log("TabsDock Dock " & MethodName & " " & Docked.RunMethod("getValue",Null))
End Sub

Set the listener after the Dock has been initialized using:

B4X:
DockNodeSetEventHandler(TabsDock,"TD")
 

stevel05

Expert
Licensed User
Longtime User
Added jDockFXCV to post 1 - CustomView Version.

Just starting to use this now, so I've wrapped a bit more of it and created a custom view so it can be added via the designer. It is similar to TabPane, in that you cannot add DockNode's to the DockPane in the designer, this still has to be done in code, but it's a bit easier with the wrapper.

I also exposed a few more methods. The original Demo is included for comparison.

Enjoy.
 
Last edited:

stevel05

Expert
Licensed User
Longtime User
Updated CustomView to v 0.02, added more methods including LoadLayout, more comments and documentation in the first post.
 

Lahksman

Active Member
Licensed User
Longtime User
First of all, thx for this lib, I'm loving it.

But I'm having some trouble setting up my nodes. I would like to achieve this when the app starts:
upload_2017-4-7_17-32-14.png


But I keep getting this:
upload_2017-4-7_17-33-6.png


This is my code:
B4X:
Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    MainForm.RootPane.LoadLayout("frmDashboard") 'Load the layout file.
    MainForm.Show
    DockNode1.Initialize(Me,"DockNode1","DockNode1",Null).PrefSize(MainForm.Width / 2, (MainForm.Height/3*2))
    DockNode1.Pane.LoadLayout("DockNode1")
    DockNode1.Dock(DockPane1,"LEFT")
    DockNode1.Tag = "DockNode1"
    DockNode2.Initialize(Me,"DockNode2","DockNode2",Null).PrefSize(MainForm.Width / 2, (MainForm.Height/3*2))
    DockNode2.Pane.LoadLayout("DockNode2")
    DockNode2.Dock(DockPane1,"RIGHT")
    DockNode2.Tag = "DockNode2"
    DockNode3.Initialize(Me,"DockNode3","DockNode3",Null).PrefSize(MainForm.Width, (MainForm.Height/3))
    DockNode3.Pane.LoadLayout("DockNode3")
    DockNode3.Dock(DockPane1,"BOTTOM")
    DockNode3.Tag = "DockNode3"
    DockPane1.SetDefaultUserAgentStylesheet
End Sub
 

stevel05

Expert
Licensed User
Longtime User
It's a while since I looked at this, but you can use the code from the example to size the nodes on first layout:

B4X:
Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form

   

    Private DockPane1 As DockPane
    Private DockNode1 As DockNode
    Private DockNode2 As DockNode
    Private DockNode3 As DockNode
   
    Private FirstResize As Boolean = True
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
   
    MainForm.RootPane.LoadLayout("1") 'Load the layout file.
    MainForm.Show
   
    CallSubDelayed(Me,setupDockNode)
   
End Sub

Sub setupDockNode
   
    DockNode1.Initialize(Me,"DockNode1","DockNode1",Null).PrefSize(MainForm.Width / 2, (MainForm.Height/3*2))
    DockNode1.Pane.LoadLayout("DockNode1")
    DockNode1.Dock(DockPane1,"LEFT")
    DockNode1.Tag = "DockNode1"
    DockNode2.Initialize(Me,"DockNode2","DockNode2",Null).PrefSize(MainForm.Width / 2, (MainForm.Height/3*2))
    DockNode2.Pane.LoadLayout("DockNode2")
    DockNode2.Dock(DockPane1,"RIGHT")
    DockNode2.Tag = "DockNode2"
    DockNode3.Initialize(Me,"DockNode3","DockNode3",Null).PrefSize(MainForm.Width, (MainForm.Height/3))
    DockNode3.Pane.LoadLayout("DockNode3")
    DockNode3.Dock(DockPane1,"BOTTOM")
    DockNode3.Tag = "DockNode3"

   
    DockPane1.SetDefaultUserAgentStylesheet

End Sub

Sub MainForm_Resize (Width As Double, Height As Double)
   
    'Hack for first setup.  We can also use this method to programatically resize the nodes at any time.
    ' We have to get the splitpane from the correct node using reflection as it is a private variable.
    If FirstResize Then
        Dim R As Reflector
        R.Target = DockPane1.AsNode
        Dim SP As SplitPane = R.GetField("root")
        SP.DividerPositions = Array As Double(0.66)
        FirstResize = False
    End If
End Sub

Hope it helps
 

stevel05

Expert
Licensed User
Longtime User
I said it was a while, just realized that the split pane is exposed in the library, so you can do:

B4X:
Sub MainForm_Resize (Width As Double, Height As Double)
   
    'Hack for first setup.  We can also use this method to programatically resize the nodes at any time.
    ' We have to get the splitpane from the correct node using reflection as it is a private variable.
    If FirstResize Then
        Dim SP As SplitPane = DockPane1.SplitPane
        SP.DividerPositions = Array As Double(0.66)
        FirstResize = False
    End If
End Sub
 

Lahksman

Active Member
Licensed User
Longtime User
Another annoying question.

I know it's possible to close a docknode with the close button. Is it also possible to close it with code? Or catch the event behind the close button?
 

stevel05

Expert
Licensed User
Longtime User
In the Javacode

https://github.com/RobertBColton/DockFX/blob/master/src/main/java/org/dockfx/DockNode.java

there is a close method which you can call via the JavaObject. So you can Add this sub to the DockNode Class

B4X:
'Close this dock node by setting it to not floating and making sure it is detached from any dock
' pane.
Sub Close
    TJO.RunMethod("close",Null)
End Sub

Which will close the Node.

To show it again you just have to dock it:

B4X:
TDock1.Dock(DockPane1,"LEFT")
 

alienhunter

Active Member
Licensed User
Longtime User
Hi Steve
When i try your DockFXCV2 demo
and undock both , and try dock only one
i get this blue square it will not go away until i dock the other one

dock1.jpg



Also when i move the main window
dock2.jpg


any clues
thanks AH
 

stevel05

Expert
Licensed User
Longtime User
Yes I can see it, unfortunately there appears to be a bug in the underlying library if the DockPane is empty (the wrapper is pretty light). You can stop this happening by ensuring there is always one node docked (Remove the dock titlebar from 1 docknode).

There doesn't appear to be a way to fix it apart from that as a null pointer exception is raised in an event handler in the underlying library.

I'm not sure if this was always there, or if it's a result of upgrades to Java. DockFX hasn't been updated for 2 years.
 
Top