B4J Code Snippet Splitter with Fixed Pane

The Java splitter has the nasty tendency of resizing both panels when the form or splitter parent is resized.
I think it is a better behaviour when we have one fixed panel and one flexible panel, so when you resize the form, one panel keeps its size and only the other one changes.
Fighting this behaviour by resetting the percentages of the split panel sizes produces some spectacular flicker on the screen while resizing.

It is never a good idea to reinvent the wheel, but after some consideration I decided to simply build a splitter class from standard B4X panes. There are some reasons for it.

- It is simple! B4X makes everything simple.
- It eliminates the flicker without going into validating/invalidating window regions and all that mess. Resizing becomes smooth.
- It gives us full control over the appearance of every part of the splitter.

So, here it is. Just four panels with anchors set in the visual designer.
We can change the splitter orientation and resize response by having a left, top, right or bottom style, meaning that those panels are fixed when the whole form is resized, they keep their size.

This can be switched at runtime, so we can allow the user to pin the size of the panes wherever they want to.

The example class also saves its splitter position in a KeyValueStore and reloads it at initialization time, if its Key string is found in the store.

The "engine" of the whole thing is the resizing process, I found the solution in Erel's CLV drag/drop example. It shows the power of resumable subs, how they make everything easy:

Splitter resize loop:
Private Sub Sizer_MousePressed (EventData As MouseEvent)
    If EventData.ClickCount > 1 Then Return
    Sizing = True
    Dim StartX As Double = EventData.X
    Dim StartY As Double = EventData.Y
    Do While Sizing
        Wait For(Sizer) Sizer_MouseDragged(EventData As MouseEvent)
        SetSizer(EventData.X - StartX, EventData.Y - StartY)
    Loop
End Sub

Private Sub SetSizer(X As Int, Y As Int)
    Select FSplitterType
        Case splLeft
            Sizer.Left = Max(Min(Sizer.Left + X, panBase.Width - FMinFlexSize), FMinFixedSize)
            FlexPanel.Left = Sizer.Left + Sizer.Width
            FlexPanel.Width = Max(FlexPanel.Parent.Width - FlexPanel.Left, FMinFlexSize)
            FixedPanel.Width = Max(Sizer.Left - FixedPanel.Left, FMinFixedSize)
        Case splRight
            Sizer.Left = Max(Min(Sizer.Left + X, panBase.Width - FMinFlexSize), FMinFixedSize)
            FlexPanel.Left = Sizer.Left + Sizer.Width
            FlexPanel.Width = FlexPanel.Parent.Width - FlexPanel.Left
            FixedPanel.Width = Sizer.Left - FixedPanel.Left
        Case splTop
            Sizer.Top = Max(Min(Sizer.Top + Y, panBase.Height - FMinFlexSize), FMinFixedSize)
            FlexPanel.Top = Sizer.Top + Sizer.Height
            FlexPanel.Height = FlexPanel.Parent.Height - FlexPanel.Top
            FixedPanel.Height = Sizer.Top - FixedPanel.Top
        Case Else
            Sizer.Top = Max(Min(Sizer.Top + Y, panBase.Height - FMinFlexSize), FMinFixedSize)
            FlexPanel.Top = Sizer.Top + Sizer.Height
            FlexPanel.Height = FlexPanel.Parent.Height - FlexPanel.Height
            FixedPanel.Height = Sizer.Top - FixedPanel.Top
    End Select
End Sub

Private Sub Sizer_MouseReleased (EventData As MouseEvent)
    Sizing = False
    B4XPages.MainPage.KVS.Put($"${OptKey}Pos"$, IIf(Horizontal, Sizer.Left, Sizer.Top))
End Sub

Seeing how easy LoadLayout and other tools make it to control the UI, it is tempting to build my own Tab control, or a CLV-tree combobox and so on. This little project was so simple, now I split my sides laughing.
 

Attachments

  • SplitterExample.zip
    267.3 KB · Views: 270
Last edited:
Top