Contribution – SplitPanel

'ello.

Another little custom component. This one implements a “Split Panel”, i.e. it can contain two children, with a divider that may be dragged. It supports both horizontal and vertical dividers, as well as one-touch expanders.

There is one little issue. When expanded to the left or the bottom, the buttons seem to lose their click area a bit. It sort of looks like an old bug, but is most likely something stupid i’ve done. I’ll keep trying to fix this, but maybe @t0neg0d has an idea?

The obligatory video …

[video]SplitPanel - YouTube

And the source …

[java]

import com.jme3.input.event.MouseButtonEvent;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector4f;
import tonegod.gui.controls.buttons.ButtonAdapter;
import tonegod.gui.core.Element;
import tonegod.gui.core.ElementManager;
import tonegod.gui.core.layouts.AbstractLayout;
import tonegod.gui.core.layouts.LayoutHelper;
import tonegod.gui.core.utils.UIDUtil;

/**

  • A container that takes two children, and presents a divider bar that may be dragged.

  • Space removed from one side is given to the other and vice versa.

  • <p>

  • Also supported are ‘one touch expander’ buttons that move the divider to the

  • extremities.

  • @author rockfire
    */
    public class SplitPanel extends Element {

    private Orientation orientation;
    private Element leftOrTop;
    private Element rightOrBottom;
    private float dividerLocation = Float.MIN_VALUE;
    private float defaultDividerLocationRatio = 0.5f;
    private final int dividerSize;
    private final ButtonAdapter divider;
    private final ButtonAdapter expandLeft;
    private final ButtonAdapter expandRight;
    private Float beforeExpand;
    private boolean useOneTouchExpanders = true;
    private final Vector4f margins;

    /**

    • Creates a new instance of the SplitPanel control
    • @param screen The screen control the Element is to be added to
    • @param orientation The orientation of the split
      */
      public SplitPanel(ElementManager screen, Orientation orientation) {
      this(screen, Vector2f.ZERO, screen.getStyle(“SplitPanel”).getVector2f(“defaultSize”), orientation);
      }

    /**

    • Creates a new instance of the SplitPanel control
    • @param screen The screen control the Element is to be added to
    • @param position A Vector2f containing the x/y position of the Element
    • @param dimensions A Vector2f containing the width/height dimensions of the Element
    • @param resizeBorders A Vector4f containing the border information used when
    • resizing the default image (x = N, y = W, z = E, w = S)
    • @param orientation The orientation of the split
      */
      public SplitPanel(ElementManager screen, Vector2f position, Vector2f dimensions, Orientation orientation) {
      this(screen, position, dimensions, screen.getStyle(“SplitPanel”).getVector4f(“resizeBorders”),
      screen.getStyle(“SplitPanel”).getString(“defaultImg”), orientation);
      }

    /**

    • Creates a new instance of the SplitPanel control

    • @param screen The screen control the Element is to be added to

    • @param position A Vector2f containing the x/y position of the Element

    • @param dimensions A Vector2f containing the width/height dimensions of the Element

    • @param resizeBorders A Vector4f containing the border information used when

    • resizing the default image (x = N, y = W, z = E, w = S)

    • @param defaultImg The default image to use for the Button

    • @param orientation The orientation of the split
      */
      public SplitPanel(ElementManager screen, Vector2f position, Vector2f dimensions, Vector4f resizeBorders, String defaultImg, Orientation orientation) {
      super(screen, UIDUtil.getUID(), position, dimensions, resizeBorders, defaultImg);
      this.orientation = orientation;

      // TODO get from style
      Vector2f min = screen.getStyle(“SplitPanel”).getVector2f(“minSize”);
      if (min != null) {
      setMinDimensions(min);
      }
      margins = screen.getStyle(“SplitPanel”).getVector4f(“margins”);
      dividerSize = screen.getStyle(“SplitPanel”).getInt(“dividerSize”);

      // Layout
      setLayout(new SplitLayout(screen));

      // Divider
      divider = new ButtonAdapter(screen) {
      private boolean dragged;

       @Override
       public void onButtonMouseLeftDown(MouseButtonEvent evt, boolean toggled) {
           dragged = false;
       }
      
       @Override
       public void onButtonMouseLeftUp(MouseButtonEvent evt, boolean toggled) {
           if (!dragged) {
               if (beforeExpand != null) {
                   dividerLocation = beforeExpand;
                   beforeExpand = null;
               } else {
                   // Expand to whichever is the closer side
                   beforeExpand = dividerLocation;
                   if (dividerLocation &gt; (SplitPanel.this.getWidth() / 2f)) {
                       dividerLocation = Float.MAX_VALUE;
                   } else {
                       dividerLocation = 0;
                   }
               }
           }
           SplitPanel.this.layoutChildren();
           onExpandLeftOrTop();
       }
      
       @Override
       public void controlMoveHook() {
           dragged = true;
           dividerMoved();
       }
      

      };
      divider.setIsMovable(true);
      divider.setMinDimensions(new Vector2f(dividerSize, dividerSize));
      divider.addClippingLayer(divider);
      addChild(divider);

      // Expand left
      expandLeft = new ButtonAdapter(screen, Vector2f.ZERO, new Vector2f(dividerSize, dividerSize)) {
      @Override
      public void onButtonMouseLeftUp(MouseButtonEvent evt, boolean toggled) {
      if (beforeExpand != null) {
      dividerLocation = beforeExpand;
      beforeExpand = null;
      } else {
      beforeExpand = dividerLocation;
      dividerLocation = 0;
      }
      SplitPanel.this.layoutChildren();
      onExpandLeftOrTop();
      }
      };
      expandLeft.setIsMovable(false);
      expandLeft.setMinDimensions(new Vector2f(dividerSize, dividerSize));
      expandLeft.addClippingLayer(expandLeft);
      addChild(expandLeft);

      // Expand right
      expandRight = new ButtonAdapter(screen, Vector2f.ZERO, new Vector2f(dividerSize, dividerSize)) {
      @Override
      public void onButtonMouseLeftUp(MouseButtonEvent evt, boolean toggled) {
      if (beforeExpand != null) {
      dividerLocation = beforeExpand;
      beforeExpand = null;
      } else {
      beforeExpand = getDividerLocation();
      dividerLocation = Float.MAX_VALUE;
      }
      SplitPanel.this.layoutChildren();
      onExpandRightOrBottom();
      }
      };
      expandRight.setIsMovable(false);
      expandRight.setMinDimensions(new Vector2f(dividerSize, dividerSize));
      expandRight.addClippingLayer(expandRight);

      // This
      addChild(expandRight);
      reconfigureDivider(orientation);
      }

    @Override
    public void controlResizeHook() {
    super.controlResizeHook();
    layoutChildren();
    }

    /**

    • Get the current divider location.
    • @return divider location
      */
      public float getDividerLocation() {
      return dividerLocation;
      }

    /**

    • Set the current divider location.
    • @param dividerLocation divider location
      */
      public void setDividerLocation(float dividerLocation) {
      this.dividerLocation = dividerLocation;
      layoutChildren();
      }

    /**

    • Get the default divider location ratio which determines where the divider starts.
    • For example, if you have a HORIZONTAL split, and set this value to
    • 0.25, the divider will start a quarter of its width from the left.
    • @return default divider location ration
    • @see #setDefaultDividerLocationRatio(float)
      */
      public float getDefaultDividerLocationRatio() {
      return defaultDividerLocationRatio;
      }

    /**

    • Set the default divider location ratio which determines where the divider starts.
    • For example, if you have a HORIZONTAL split, and set this value to
    • 0.25, the divider will start a quarter of its width from the left.
    • @param defaultDividerLocationRatio default divider location ration
    • @see #setDefaultDividerLocationRatio(float)
      */
      public void setDefaultDividerLocationRatio(float defaultDividerLocationRatio) {
      this.defaultDividerLocationRatio = defaultDividerLocationRatio;
      layoutChildren();
      }

    /**

    • Set the element that is placed in the left hand side (for HORIZONTAL orientation)

    • or the top (for VERTICAL orientation).

    • @param leftOrTop left or top element
      */
      public void setLeftOrTop(Element leftOrTop) {
      if (this.leftOrTop != null) {
      removeChild(leftOrTop);
      }
      this.leftOrTop = leftOrTop;

      leftOrTop.addClippingLayer(leftOrTop);
      addChild(leftOrTop);
      layoutChildren();
      }

    /**

    • Set the element that is placed in the right hand side (for HORIZONTAL orientation)
    • or the bottom (for VERTICAL orientation).
    • @param rightOrBottom right or bottom element
      */
      public void setRightOrBottom(Element rightOrBottom) {
      if (this.rightOrBottom != null) {
      removeChild(rightOrBottom);
      }
      this.rightOrBottom = rightOrBottom;
      rightOrBottom.addClippingLayer(rightOrBottom);
      addChild(rightOrBottom);
      layoutChildren();
      }

    /**

    • Get the element that is placed in the left hand side (for HORIZONTAL orientation)
    • or the top (for VERTICAL orientation).
    • @param leftOrTop left or top element
      */
      public Element getLeftOrTop() {
      return leftOrTop;
      }

    /**

    • Get the element that is placed in the right hand side (for HORIZONTAL orientation)
    • or the bottom (for VERTICAL orientation).
    • @return right or bottom element
      */
      public Element getRightOrBottom() {
      return rightOrBottom;
      }

    /**

    • Get the orientation.
    • @return orientation
      */
      public Orientation getOrientation() {
      return orientation;
      }

    /**

    • Set the orientation.
    • @param orientation orientation
      */
      public void setOrientation(Orientation orientation) {
      this.orientation = orientation;
      reconfigureDivider(orientation);
      layoutChildren();
      }

    /**

    • Set whether the one touch expander buttons are visible.
    • @param useOneTouchExpanders use one touch expanders
      */
      public void setUseOneTouchExpanders(boolean useOneTouchExpanders) {
      if (useOneTouchExpanders != this.useOneTouchExpanders) {
      this.useOneTouchExpanders = useOneTouchExpanders;
      expandLeft.setIsVisible(useOneTouchExpanders);
      expandRight.setIsVisible(useOneTouchExpanders);
      layoutChildren();
      }
      }

    /**

    • Get whether the one touch expander buttons are visible.
    • @return use one touch expanders
      */
      public boolean getUseOneTouchExpanders(boolean useOneTouchExpanders) {
      return useOneTouchExpanders;
      }

    /**

    • Invoked when the expanders are used to expand to the right or bottom.

    */
    protected void onExpandRightOrBottom() {
    // For sub-classes to override
    }

    /**

    • Invoked when the expanders are used to expand to the left or top
      */
      protected void onExpandLeftOrTop() {
      // For sub-classes to override
      }

    /**

    • Invoked when the divider is moved
      */
      protected void onDividerMoved() {
      // For sub-classes to override
      }

    protected void dividerMoved() {
    beforeExpand = null;
    dividerLocation = orientation.equals(Orientation.HORIZONTAL) ? divider.getX() : divider.getY();
    layoutChildren();
    onDividerMoved();
    }

    private void reconfigureDivider(Orientation orientation) {
    final boolean v = orientation.equals(Orientation.VERTICAL);
    divider.setResizeN(v);
    divider.setResizeS(v);
    divider.setResizeE(!v);
    divider.setResizeW(!v);
    expandLeft.setButtonIcon(dividerSize / 2f, dividerSize / 2f, screen.getStyle(“Common”).getString(v ? “arrowDown” : “arrowLeft”));
    expandRight.setButtonIcon(dividerSize / 2f, dividerSize / 2f, screen.getStyle(“Common”).getString(v ? “arrowUp” : “arrowRight”));
    String styleName = “SplitPanel#Divider#” + orientation.name();
    divider.borders.set(screen.getStyle(styleName).getVector4f(“resizeBorders”));
    divider.setColorMap(screen.getStyle(styleName).getString(“defaultImg”));
    divider.setButtonHoverInfo(screen.getStyle(styleName).getString(“hoverImg”), screen.getStyle(styleName).getColorRGBA(“hoverColor”));
    divider.setButtonPressedInfo(screen.getStyle(styleName).getString(“pressedImg”), screen.getStyle(styleName).getColorRGBA(“pressedrColor”));
    }

    class SplitLayout extends AbstractLayout {

     public SplitLayout(ElementManager screen) {
         super(screen);
         margins.set(SplitPanel.this.margins);
     }
    
     public void resize() {
         layoutChildren();
     }
    
     public void setOwner(Element owner) {
         this.owner = owner;
     }
    
     public void layoutChildren() {
         float space;
         float actualDividerLocation;
         float leftOrTopWidth;
         float rightOrBottomWidth;
         float childSize;
         LayoutHelper.reset();
         LayoutHelper.setPadding(padding.x, padding.y, padding.x, padding.w);
         LayoutHelper.advanceX(margins.x);
         LayoutHelper.advanceY(margins.y);
         if (orientation.equals(Orientation.HORIZONTAL)) {
             // Space available
             // Keep divider within bounds
             if (dividerLocation != Float.MIN_VALUE) {
                 if (dividerLocation &lt; margins.y) {
                     dividerLocation = margins.y;
                 } else if (dividerLocation &gt; owner.getWidth() - dividerSize - margins.z) {
                     dividerLocation = owner.getWidth() - dividerSize - margins.z;
                 }
                 actualDividerLocation = (int) (dividerLocation);
             } else {
                 actualDividerLocation = (int) (owner.getWidth() * defaultDividerLocationRatio);
             }
             // Sizes
             leftOrTopWidth = actualDividerLocation - margins.y;
             rightOrBottomWidth = owner.getWidth() - actualDividerLocation - margins.z - dividerSize;
    
             childSize = owner.getHeight() - margins.x - margins.w;
    
             // Do the actual layout
             // Layout left
             if (leftOrTop != null) {
                 if (leftOrTopWidth &gt; 0) {
                     leftOrTop.show();
                     leftOrTop.setPosition(LayoutHelper.position());
                     leftOrTop.resize((int) (leftOrTop.getAbsoluteX() + leftOrTopWidth), (int) (leftOrTop.getAbsoluteY() + childSize), Element.Borders.SE);
                     LayoutHelper.advanceX(leftOrTopWidth);
                 } else {
                     leftOrTop.hide();
                 }
             }
             // Layout divider
             if (useOneTouchExpanders) {
                 expandLeft.setPosition(LayoutHelper.position());
                 LayoutHelper.advanceY(dividerSize);
                 expandRight.setPosition(LayoutHelper.position());
                 LayoutHelper.advanceY(dividerSize);
             }
             divider.setPosition(LayoutHelper.position());
             divider.setDimensions(dividerSize, childSize - (useOneTouchExpanders ? dividerSize * 2 : 0));
    
             if (useOneTouchExpanders) {
                 LayoutHelper.advanceY(-(dividerSize * 2));
             }
             LayoutHelper.advanceX(dividerSize);
             // Layout right
             if (rightOrBottom != null) {
                 if (rightOrBottomWidth &gt; 0) {
                     rightOrBottom.show();
                     rightOrBottom.setPosition(LayoutHelper.position());
                     rightOrBottom.resize((int) (rightOrBottom.getAbsoluteX() + rightOrBottomWidth), (int) (rightOrBottom.getAbsoluteY() + childSize), Element.Borders.SE);
                     LayoutHelper.advanceX(rightOrBottomWidth);
                 } else {
                     rightOrBottom.hide();
                 }
             }
         } else {
             // Space available
             space = owner.getHeight() - dividerSize;
             // Keep divider within bounds
             if (dividerLocation != Float.MIN_VALUE) {
                 if (dividerLocation &lt; margins.x) {
                     dividerLocation = margins.x;
                 } else if (dividerLocation &gt; owner.getHeight() - dividerSize - margins.w) {
                     dividerLocation = owner.getHeight() - dividerSize - margins.w;
                 }
                 actualDividerLocation = (int) (dividerLocation);
             } else {
                 actualDividerLocation = (int) (space - (space * defaultDividerLocationRatio));
             }
    
             // Sizes
             leftOrTopWidth = actualDividerLocation - margins.x;
             rightOrBottomWidth = owner.getHeight() - actualDividerLocation - margins.w - dividerSize;
             childSize = owner.getWidth() - margins.y - margins.z;
    
             // Left
             if (rightOrBottom != null) {
                 rightOrBottom.setPosition(LayoutHelper.position());
                 rightOrBottom.resize((int) (leftOrTop.getAbsoluteX() + childSize), (int) (rightOrBottom.getAbsoluteY() + leftOrTopWidth), Element.Borders.SE);
                 LayoutHelper.advanceY(leftOrTopWidth);
             }
             // Layout divider
             if (useOneTouchExpanders) {
                 expandLeft.setPosition(LayoutHelper.position());
                 LayoutHelper.advanceX(dividerSize);
                 expandRight.setPosition(LayoutHelper.position());
                 LayoutHelper.advanceX(dividerSize);
             }
             divider.setPosition(LayoutHelper.position());
             divider.setDimensions(childSize - (useOneTouchExpanders ? dividerSize * 2 : 0), dividerSize);
             LayoutHelper.advanceY(dividerSize);
             if (useOneTouchExpanders) {
                 LayoutHelper.advanceX(-(dividerSize * 2));
             }
             // Layout right
             if (leftOrTop != null) {
                 leftOrTop.setPosition(LayoutHelper.position());
                 leftOrTop.resize((int) (leftOrTop.getAbsoluteX() + childSize), (int) (leftOrTop.getAbsoluteY() + rightOrBottomWidth), Element.Borders.SE);
                 LayoutHelper.advanceY(rightOrBottomWidth);
             }
         }
         owner.updateClippingLayers();
         // Clean up
         LayoutHelper.reset();
     }
    
     protected Vector4f getMargins() {
         return margins;
     }
    

    }
    }

[/java]

And the style XML

[java]

&lt;element name="SplitPanel"&gt;
    &lt;font&gt;&lt;/font&gt;
    &lt;attributes&gt;
        &lt;property name="resizeBorders" type="Vector4f"&gt;
            &lt;x value="10" /&gt;
            &lt;y value="10" /&gt;
            &lt;z value="10" /&gt;
            &lt;w value="10" /&gt;
        &lt;/property&gt;
        &lt;property name="margins" type="Vector4f"&gt;
            &lt;x value="4" /&gt;
            &lt;y value="4" /&gt;
            &lt;z value="4" /&gt;
            &lt;w value="4" /&gt;
        &lt;/property&gt;
        &lt;property name="defaultSize" type="Vector2f"&gt;
            &lt;x value="350" /&gt;
            &lt;y value="200" /&gt;
        &lt;/property&gt;
        &lt;property name="dividerSize" type="int" value="16" /&gt;
    &lt;/attributes&gt;
    &lt;images&gt;
        &lt;property name="defaultImg" type="String" value="tonegod/gui/style/def/Window/panel_x.png" /&gt;
    &lt;/images&gt;
    &lt;effects&gt;
    &lt;/effects&gt;
&lt;/element&gt;

&lt;element name="SplitPanel#Divider#HORIZONTAL"&gt;
    &lt;font/&gt;
    &lt;attributes&gt;
        &lt;property name="resizeBorders" type="Vector4f"&gt;
            &lt;x value="4" /&gt;
            &lt;y value="4" /&gt;
            &lt;z value="4" /&gt;
            &lt;w value="4" /&gt;
        &lt;/property&gt;
    &lt;/attributes&gt;
    &lt;images&gt;
        &lt;property name="defaultImg" type="String" value="tonegod/gui/style/def/Button/button_x_u.png" /&gt;
        &lt;property name="hoverImg" type="String" value="tonegod/gui/style/def/Button/button_x_h.png" /&gt;
        &lt;property name="pressedImg" type="String" value="tonegod/gui/style/def/Button/button_x_d.png" /&gt;
    &lt;/images&gt;
    &lt;effects&gt;&lt;/effects&gt;
&lt;/element&gt;

&lt;element name="SplitPanel#Divider#VERTICAL"&gt;
    &lt;font/&gt;
    &lt;attributes&gt;
        &lt;property name="resizeBorders" type="Vector4f"&gt;
            &lt;x value="4" /&gt;
            &lt;y value="4" /&gt;
            &lt;z value="4" /&gt;
            &lt;w value="4" /&gt;
        &lt;/property&gt;
    &lt;/attributes&gt;
    &lt;images&gt;
        &lt;property name="defaultImg" type="String" value="tonegod/gui/style/def/Button/button_x_u.png" /&gt;
        &lt;property name="hoverImg" type="String" value="tonegod/gui/style/def/Button/button_x_h.png" /&gt;
        &lt;property name="pressedImg" type="String" value="tonegod/gui/style/def/Button/button_x_d.png" /&gt;
    &lt;/images&gt;
    &lt;effects&gt;&lt;/effects&gt;
&lt;/element&gt;

[/java]

Any comments or feature requests?

RR.

5 Likes

Ooooo! This is cool!

I wanted to do this when I originally put together the library… but it was a failure with a side of failure until Layouts were added.

Got an update for this. A few recent changes required some tweaks to this control. Mainly minimum sizes. These are now respected, so you cannot shrink a panel less than it’s minimum dimensions. You can of course set min dimensions to zero to turn this off.

[java]
package tonegod.gui.controls.extras;

import com.jme3.input.event.MouseButtonEvent;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector4f;
import tonegod.gui.controls.buttons.ButtonAdapter;
import tonegod.gui.core.Element;
import tonegod.gui.core.ElementManager;
import tonegod.gui.core.layouts.AbstractLayout;
import tonegod.gui.core.layouts.LayoutHelper;
import tonegod.gui.core.utils.UIDUtil;

/**

  • A container that takes two children, and presents a divider bar that may be dragged.

  • Space removed from one side is given to the other and vice versa.

  • <p>

  • Also supported are ‘one touch expander’ buttons that move the divider to the

  • extremities.

  • @author rockfire
    */
    public class SplitPanel extends Element {

    protected Orientation orientation;
    protected Element leftOrTop;
    protected Element rightOrBottom;
    protected float dividerLocation = Float.MIN_VALUE;
    protected float defaultDividerLocationRatio = 0.5f;
    protected final ButtonAdapter divider;
    protected final ButtonAdapter expandLeft;
    protected final ButtonAdapter expandRight;
    protected Float beforeExpand;
    protected boolean useOneTouchExpanders = true;
    protected final int dividerSize;

    /**

    • Creates a new instance of the SplitPanel control
    • @param screen The screen control the Element is to be added to
    • @param orientation The orientation of the split
      */
      public SplitPanel(ElementManager screen, Orientation orientation) {
      this(screen, Vector2f.ZERO, screen.getStyle(“SplitPanel”).getVector2f(“defaultSize”), orientation);
      }

    /**

    • Creates a new instance of the SplitPanel control
    • @param screen The screen control the Element is to be added to
    • @param position A Vector2f containing the x/y position of the Element
    • @param dimensions A Vector2f containing the width/height dimensions of the Element
    • @param resizeBorders A Vector4f containing the border information used when
    • resizing the default image (x = N, y = W, z = E, w = S)
    • @param orientation The orientation of the split
      */
      public SplitPanel(ElementManager screen, Vector2f position, Vector2f dimensions, Orientation orientation) {
      this(screen, position, dimensions, screen.getStyle(“SplitPanel”).getVector4f(“resizeBorders”),
      screen.getStyle(“SplitPanel”).getString(“defaultImg”), orientation);
      }

    /**

    • Creates a new instance of the SplitPanel control

    • @param screen The screen control the Element is to be added to

    • @param position A Vector2f containing the x/y position of the Element

    • @param dimensions A Vector2f containing the width/height dimensions of the Element

    • @param resizeBorders A Vector4f containing the border information used when

    • resizing the default image (x = N, y = W, z = E, w = S)

    • @param defaultImg The default image to use for the Button

    • @param orientation The orientation of the split
      */
      public SplitPanel(ElementManager screen, Vector2f position, Vector2f dimensions, Vector4f resizeBorders, String defaultImg, Orientation orientation) {
      super(screen, UIDUtil.getUID(), position, dimensions, resizeBorders, defaultImg);
      this.orientation = orientation;

      // TODO get from style
      Vector2f min = screen.getStyle(“SplitPanel”).getVector2f(“minSize”);
      if (min != null) {
      setMinDimensions(min);
      }

      setTextPaddingByKey(“SplitPanel”, “textPadding”);
      dividerSize = screen.getStyle(“SplitPanel”).getInt(“dividerSize”);

      // Layout
      setLayout(new SplitLayout(screen));

      // Divider
      divider = new ButtonAdapter(screen) {
      private boolean dragged;

       @Override
       public void onButtonMouseLeftDown(MouseButtonEvent evt, boolean toggled) {
           dragged = false;
       }
      
       @Override
       public void onButtonMouseLeftUp(MouseButtonEvent evt, boolean toggled) {
           if (!dragged) {
               if (beforeExpand != null) {
                   dividerLocation = beforeExpand;
                   beforeExpand = null;
               } else {
                   // Expand to whichever is the closer side
                   beforeExpand = dividerLocation;
                   if (dividerLocation &gt; (SplitPanel.this.getWidth() / 2f)) {
                       dividerLocation = Float.MAX_VALUE;
                   } else {
                       dividerLocation = 0;
                   }
               }
           }
           SplitPanel.this.layoutChildren();
           onExpandLeftOrTop();
       }
      
       @Override
       public void controlMoveHook() {
           dragged = true;
           dividerMoved();
       }
      

      };
      divider.setIsMovable(true);
      divider.setMinDimensions(new Vector2f(dividerSize, dividerSize));
      divider.addClippingLayer(divider);
      addChild(divider);

      // Expand left
      expandLeft = new ButtonAdapter(screen, Vector2f.ZERO, new Vector2f(dividerSize, dividerSize)) {
      @Override
      public void onButtonMouseLeftUp(MouseButtonEvent evt, boolean toggled) {
      if (beforeExpand != null) {
      dividerLocation = beforeExpand;
      beforeExpand = null;
      } else {
      beforeExpand = dividerLocation;
      dividerLocation = 0;
      }
      SplitPanel.this.layoutChildren();
      onExpandLeftOrTop();
      }
      };
      expandLeft.setIsMovable(false);
      expandLeft.setMinDimensions(new Vector2f(dividerSize, dividerSize));
      expandLeft.addClippingLayer(expandLeft);
      addChild(expandLeft);

      // Expand right
      expandRight = new ButtonAdapter(screen, Vector2f.ZERO, new Vector2f(dividerSize, dividerSize)) {
      @Override
      public void onButtonMouseLeftUp(MouseButtonEvent evt, boolean toggled) {
      if (beforeExpand != null) {
      dividerLocation = beforeExpand;
      beforeExpand = null;
      } else {
      beforeExpand = getDividerLocation();
      dividerLocation = Float.MAX_VALUE;
      }
      SplitPanel.this.layoutChildren();
      onExpandRightOrBottom();
      }
      };
      expandRight.setIsMovable(false);
      expandRight.setMinDimensions(new Vector2f(dividerSize, dividerSize));
      expandRight.addClippingLayer(expandRight);

      // This
      addChild(expandRight);
      reconfigureDivider(orientation);
      }

    @Override
    public void controlResizeHook() {
    super.controlResizeHook();
    layoutChildren();
    }

    /**

    • Get the current divider location.
    • @return divider location
      */
      public float getDividerLocation() {
      return dividerLocation;
      }

    /**

    • Set the current divider location.
    • @param dividerLocation divider location
      */
      public void setDividerLocation(float dividerLocation) {
      this.dividerLocation = dividerLocation;
      layoutChildren();
      }

    /**

    • Get the default divider location ratio which determines where the divider starts.
    • For example, if you have a HORIZONTAL split, and set this value to
    • 0.25, the divider will start a quarter of its width from the left.
    • @return default divider location ration
    • @see #setDefaultDividerLocationRatio(float)
      */
      public float getDefaultDividerLocationRatio() {
      return defaultDividerLocationRatio;
      }

    /**

    • Set the default divider location ratio which determines where the divider starts.
    • For example, if you have a HORIZONTAL split, and set this value to
    • 0.25, the divider will start a quarter of its width from the left.
    • @param defaultDividerLocationRatio default divider location ration
    • @see #setDefaultDividerLocationRatio(float)
      */
      public void setDefaultDividerLocationRatio(float defaultDividerLocationRatio) {
      this.defaultDividerLocationRatio = defaultDividerLocationRatio;
      layoutChildren();
      }

    /**

    • Set the element that is placed in the left hand side (for HORIZONTAL orientation)

    • or the top (for VERTICAL orientation).

    • @param leftOrTop left or top element
      */
      public void setLeftOrTop(Element leftOrTop) {
      if (this.leftOrTop != null) {
      removeChild(leftOrTop);
      }
      this.leftOrTop = leftOrTop;

      leftOrTop.addClippingLayer(leftOrTop);
      addChild(leftOrTop);
      layoutChildren();
      }

    /**

    • Set the element that is placed in the right hand side (for HORIZONTAL orientation)
    • or the bottom (for VERTICAL orientation).
    • @param rightOrBottom right or bottom element
      */
      public void setRightOrBottom(Element rightOrBottom) {
      if (this.rightOrBottom != null) {
      removeChild(rightOrBottom);
      }
      this.rightOrBottom = rightOrBottom;
      rightOrBottom.addClippingLayer(rightOrBottom);
      addChild(rightOrBottom);
      layoutChildren();
      }

    /**

    • Get the element that is placed in the left hand side (for HORIZONTAL orientation)
    • or the top (for VERTICAL orientation).
    • @param leftOrTop left or top element
      */
      public Element getLeftOrTop() {
      return leftOrTop;
      }

    /**

    • Get the element that is placed in the right hand side (for HORIZONTAL orientation)
    • or the bottom (for VERTICAL orientation).
    • @return right or bottom element
      */
      public Element getRightOrBottom() {
      return rightOrBottom;
      }

    /**

    • Get the orientation.
    • @return orientation
      */
      public Orientation getOrientation() {
      return orientation;
      }

    /**

    • Set the orientation.
    • @param orientation orientation
      */
      public void setOrientation(Orientation orientation) {
      this.orientation = orientation;
      reconfigureDivider(orientation);
      layoutChildren();
      }

    /**

    • Set whether the one touch expander buttons are visible.
    • @param useOneTouchExpanders use one touch expanders
      */
      public void setUseOneTouchExpanders(boolean useOneTouchExpanders) {
      if (useOneTouchExpanders != this.useOneTouchExpanders) {
      this.useOneTouchExpanders = useOneTouchExpanders;
      expandLeft.setIsVisible(useOneTouchExpanders);
      expandRight.setIsVisible(useOneTouchExpanders);
      layoutChildren();
      }
      }

    /**

    • Get whether the one touch expander buttons are visible.
    • @return use one touch expanders
      */
      public boolean getUseOneTouchExpanders(boolean useOneTouchExpanders) {
      return useOneTouchExpanders;
      }

    /**

    • Invoked when the expanders are used to expand to the right or bottom.

    */
    protected void onExpandRightOrBottom() {
    // For sub-classes to override
    }

    /**

    • Invoked when the expanders are used to expand to the left or top
      */
      protected void onExpandLeftOrTop() {
      // For sub-classes to override
      }

    /**

    • Invoked when the divider is moved
      */
      protected void onDividerMoved() {
      // For sub-classes to override
      }

    protected void dividerMoved() {
    beforeExpand = null;
    dividerLocation = orientation.equals(Orientation.HORIZONTAL) ? divider.getX() : divider.getY();
    layoutChildren();
    onDividerMoved();
    }

    private void reconfigureDivider(Orientation orientation) {
    final boolean v = orientation.equals(Orientation.VERTICAL);
    divider.setResizeN(v);
    divider.setResizeS(v);
    divider.setResizeE(!v);
    divider.setResizeW(!v);
    expandLeft.setButtonIcon(dividerSize / 2f, dividerSize / 2f, screen.getStyle(“Common”).getString(v ? “arrowDown” : “arrowLeft”));
    expandRight.setButtonIcon(dividerSize / 2f, dividerSize / 2f, screen.getStyle(“Common”).getString(v ? “arrowUp” : “arrowRight”));
    String styleName = “SplitPanel#Divider#” + orientation.name();
    divider.borders.set(screen.getStyle(styleName).getVector4f(“resizeBorders”));
    divider.setColorMap(screen.getStyle(styleName).getString(“defaultImg”));
    divider.setButtonHoverInfo(screen.getStyle(styleName).getString(“hoverImg”), screen.getStyle(styleName).getColorRGBA(“hoverColor”));
    divider.setButtonPressedInfo(screen.getStyle(styleName).getString(“pressedImg”), screen.getStyle(styleName).getColorRGBA(“pressedrColor”));
    }

    class SplitLayout extends AbstractLayout {

     public SplitLayout(ElementManager screen) {
         super(screen);
         margins = getTextPaddingVec();
     }
    
     @Override
     public void resize() {
         layoutChildren();
     }
    
     @Override
     public void setOwner(Element owner) {
         this.owner = owner;
     }
    
     @Override
     public void layoutChildren() {
         float space;
         float actualDividerLocation;
         float childSize;
         LayoutHelper.reset();
         LayoutHelper.setPadding(padding.x, padding.y, padding.x, padding.w);
         LayoutHelper.advanceX(margins.x);
         LayoutHelper.advanceY(margins.y);
         
         if (orientation.equals(Orientation.HORIZONTAL)) {
             // Keep divider within bounds of the whole area
             if (dividerLocation != Float.MIN_VALUE &amp;&amp; owner.getInitialized()) {
                 if (dividerLocation &lt; margins.y) {
                     dividerLocation = margins.y;
                 } else if (dividerLocation &gt; owner.getWidth() - dividerSize - margins.z) {
                     dividerLocation = owner.getWidth() - dividerSize - margins.z;
                 }
                 actualDividerLocation = (int) (dividerLocation);
             } else {
                 actualDividerLocation = (int) (owner.getWidth() * defaultDividerLocationRatio);
             }
             // Sizes
             float leftWidth = actualDividerLocation - margins.y;
             float rightWidth = owner.getWidth() - actualDividerLocation - margins.z - dividerSize;
    
             childSize = owner.getHeight() - margins.x - margins.w;
    
             if (rightOrBottom != null) {
                 if (rightWidth &lt; rightOrBottom.getMinDimensions().y) {
                     // Keep right bigger than its minimum size
                     float adj = rightOrBottom.getMinDimensions().y - rightWidth;
                     rightWidth = rightOrBottom.getMinDimensions().y;
                     leftWidth -= adj;
                 }
             }
    
    
             // Layout left
             if (leftOrTop != null) {
    
                 if (leftWidth &lt; leftOrTop.getMinDimensions().y) {
                     // Keep left bigger than its minimum size
                     float adj = leftOrTop.getMinDimensions().y - leftWidth;
                     leftWidth = leftOrTop.getMinDimensions().y;
                     rightWidth -= adj;
                 }
    
    
                 if (leftWidth &gt; 0) {
                     leftOrTop.show();
                     leftOrTop.setPosition(LayoutHelper.position());
                     leftOrTop.resize((int) (leftOrTop.getAbsoluteX() + leftWidth), (int) (leftOrTop.getAbsoluteY() + childSize), Element.Borders.SE);
                     LayoutHelper.advanceX(leftWidth);
                 } else {
                     leftOrTop.hide();
                 }
             }
    
    
             // Layout divider
             if (useOneTouchExpanders) {
                 expandLeft.setPosition(LayoutHelper.position());
                 LayoutHelper.advanceY(dividerSize);
                 expandRight.setPosition(LayoutHelper.position());
                 LayoutHelper.advanceY(dividerSize);
             }
             divider.setPosition(LayoutHelper.position());
             divider.setDimensions(dividerSize, childSize - (useOneTouchExpanders ? dividerSize * 2 : 0));
    
             if (useOneTouchExpanders) {
                 LayoutHelper.advanceY(-(dividerSize * 2));
             }
             LayoutHelper.advanceX(dividerSize);
             
             // Layout right
             if (rightOrBottom != null) {
                 if (rightWidth &gt; 0) {
                     rightOrBottom.show();
                     rightOrBottom.setPosition(LayoutHelper.position());
                     rightOrBottom.resize((int) (rightOrBottom.getAbsoluteX() + rightWidth), (int) (rightOrBottom.getAbsoluteY() + childSize), Element.Borders.SE);
                     LayoutHelper.advanceX(rightWidth);
                 } else {
                     rightOrBottom.hide();
                 }
             }
         } else {
             // Space available
             space = owner.getHeight() - dividerSize;
             
             // Keep divider within bounds of the whole area
             if (dividerLocation != Float.MIN_VALUE &amp;&amp; owner.getInitialized()) {
                 if (dividerLocation &lt; margins.x) {
                     dividerLocation = margins.x;
                 } else if (dividerLocation &gt; owner.getHeight() - dividerSize - margins.w) {
                     dividerLocation = owner.getHeight() - dividerSize - margins.w;
                 }
                 actualDividerLocation = (int) (dividerLocation);
             } else {
                 actualDividerLocation = (int) (space - (space * defaultDividerLocationRatio));
             }
    
             // Sizes to use (may get adjusted)
             float bottomHeight = actualDividerLocation - margins.x;
             float topHeight = owner.getHeight() - actualDividerLocation - margins.w - dividerSize;
             childSize = owner.getWidth() - margins.y - margins.z;
    
             if (leftOrTop != null) {
                 if (topHeight &lt; leftOrTop.getMinDimensions().y) {
                     // Keep top bigger than it's minimum size
                     float adj = leftOrTop.getMinDimensions().y - topHeight;
                     topHeight = leftOrTop.getMinDimensions().y;
                     bottomHeight -= adj;
                 }
             }
    
             // Layout Bottom (we do this first because Y coordinate is reversed)
             if (rightOrBottom != null) {
                 if (bottomHeight &lt; rightOrBottom.getMinDimensions().y) {
                     // Keep bottom bigger than it's minimum size
                     float adj = rightOrBottom.getMinDimensions().y - bottomHeight;
                     bottomHeight = rightOrBottom.getMinDimensions().y;
                     topHeight -= adj;
                 }
    
                 rightOrBottom.setPosition(LayoutHelper.position());
                 rightOrBottom.resize((int) (leftOrTop.getAbsoluteX() + childSize), (int) (rightOrBottom.getAbsoluteY() + bottomHeight), Element.Borders.SE);
                 LayoutHelper.advanceY(bottomHeight);
             }
    
    
             // Layout divider
             if (useOneTouchExpanders) {
                 expandLeft.setPosition(LayoutHelper.position());
                 LayoutHelper.advanceX(dividerSize);
                 expandRight.setPosition(LayoutHelper.position());
                 LayoutHelper.advanceX(dividerSize);
             }
             divider.setPosition(LayoutHelper.position());
             divider.setDimensions(childSize - (useOneTouchExpanders ? dividerSize * 2 : 0), dividerSize);
             LayoutHelper.advanceY(dividerSize);
             if (useOneTouchExpanders) {
                 LayoutHelper.advanceX(-(dividerSize * 2));
             }
             
             // Layout top
             if (leftOrTop != null) {// Check minimum sizes
                 leftOrTop.setPosition(LayoutHelper.position());
                 leftOrTop.resize((int) (leftOrTop.getAbsoluteX() + childSize), (int) (leftOrTop.getAbsoluteY() + topHeight), Element.Borders.SE);
                 LayoutHelper.advanceY(topHeight);
             }
         }
         owner.updateClippingLayers();
         // Clean up
         LayoutHelper.reset();
     }
    
     protected Vector4f getMargins() {
         return margins;
     }
    

    }
    }

[/java]