Creating a Bordered Panel in JavaFX

Saturday, August 22, 2009

Recently I was looking for an easy, relatively general way to visually separate arbitrary groups of components with a border. Behold the BorderPanel:

BorderPanel is simply a CustomNode that wraps any number of nodes in a Panel (which is a custom layout Container), and sticks a border around it.

Here would be the code to generate the above layout (assuming that the widgets were contained within the “vbox” object:

BorderPanel {
  borderColor: Color.DARKSLATEGRAY
  borderWidth:10
  layoutPadding: 10
  content: [ vbox ]
}

The above layout is alright, but really I wanted it to put a border around some AnimatedCharts from the previous post:

Since I can put a border around anything now though, separating groups of charts is pretty straight forward:

Which is just two BorderPanels contained in an HBox:

HBox {
content: [
  BorderPanel {
    content: HBox { content: [chart,chart1] }
  },
  BorderPanel {
    content: HBox {content: [chart2] }
  }
 ]
}

BorderPanel itself is pretty small. Here’s the create function:

override public function create():Node {
  panel = Panel {
    content: bind [border, content]
    onLayout: function() {
      for (c in content) {
        c.layoutX = borderWidth + layoutPadding;
        c.layoutY = borderWidth + layoutPadding;
      }
      updateBorder();
    }
  }
}

It simply creates the wrapper panel, “prepending” the border to the user added content (so that it always appears behind the components). The “onLayout” function is defined to move the “layoutX/Y” of each component out of the way of the border, plus some user-defined padding.

Here’s the function which creates the border, which is called whenever any of the dependent variables are updated.

function updateBorder() {
  var width = panel.width + borderWidth * 2 + layoutPadding * 2;
  var height = panel.height + borderWidth * 2 + layoutPadding * 2;
  var rect = Rectangle {
    width: width
    height: height
  }
  var innerRect = Rectangle {
    width: width - borderWidth * 2
    height: height - borderWidth * 2
    x: borderWidth
    y: borderWidth
    arcWidth: 30
    arcHeight: 30
  }
  border = ShapeSubtract {
    fill: borderColor
    a: rect
    b: innerRect
    effect: DropShadow {}
  }
}

The border itself is a shape subtraction of a rounded rectangle from a larger rectangle which encompasses the size of the entire panel. There is also a drop shadow added to give a subtle sense of depth.

The idea of creating a border that looks like this was actually inspired by this year’s Extreme GUI Makeover at JavaOne, which featured an Email client written partly in JavaFX.

Top