Enable Constraints Layout

This document describes the constraints-based layout system that is being proposed as the new layout model going forward. Familiarity with Enaml and its layout system is helpful but not required.

Using Constraints

ConstraintsContainer is a Container subclass which uses the Cassowary constraint solver to determine the layout of its child Component instances. This is achieved by adding constraint variables to the Component class which define a simple box model:

  • layout_height: The height of the component.
  • layout_width: The width of the component.
  • left: The left edge of the component.
  • right: The right edge of the component.
  • top: The top edge of the component.
  • bottom: The bottom edge of the component.
  • h_center: The vertical center line between the left and right edges
  • v_center: The horizontal center line between the top and bottom edges

Additionally, there are some constraints which only exist on ConstraintsContainer:

  • contents_height: The height of the container.
  • contents_width: The width of the container.
  • contents_left: The left edge of the container.
  • contents_right: The right edge of the container.
  • contents_top: The top edge of the container.
  • contents_bottom: The bottom edge of the container.
  • contents_h_center: The vertical center line of the container.
  • contents_v_center: The horizontal center line of the container.

These variables can be used in linear inequality expressions which make up the layout constraints of a container:

def build_hierarchy():
  container = ConstraintsContainer()
  one = Component()
  two = Component()
  container.add(one, two)
  container.layout_constraints = [
      one.layout_width == two.layout_width * 2.0,
      one.layout_height == two.layout_height,
      # ... and so on ...
  ]

  return container

For more complicated layouts, the layout_constraints trait on a ConstraintsContainer can be a callable. The function is passed a reference to the container and should return a list of LinearContraints objects or layout helper instances (as described below).

 def create_container(self):
  self.container = ConstraintsContainer()
  self.container.add(self.bar)
  self.container.layout_constraints = self.my_layout_constraints

def my_layout_constraints(self, container):
  cns = []

  if self.foo:
    cns.append(self.foo.layout_height <= 300)
    cns.append(hbox(self.foo, self.bar))
  cns.append(self.bar.layout_width == 250)

  return cns

If layout_constraints is callable, it will be invoked each time a component is added to the container or whenever the layout_size_hint trait changes on a child component.

Layout Helpers

In practice, it’s too tedious to specify all the constraints for a rich UI layout. To aid in the generation of layouts, the layout helpers from Enaml are also available in Enable. The layout helpers are:

spacer: Creates space between two adjacent components.

horizontal(*components[, spacing=10])

Takes a list of components and lines them up using their left and right edges.

Parameters:
  • components – A sequence of Component or spacer objects.
  • spacing (integer >= 0) – How many pixels of inter-element spacing to use
vertical(*components[, spacing=10])

Takes a list of components and lines them up using their top and bottom edges.

Parameters:
  • components – A sequence of Component or spacer objects.
  • spacing (integer >= 0) – How many pixels of inter-element spacing to use
hbox(*components[, spacing=10, margins=...])

Like horizontal(), but ensures the height of components matches the container.

Parameters:
  • components – A sequence of Component or spacer objects.
  • spacing (integer >= 0) – How many pixels of inter-element spacing to use
  • margins – An int, tuple of ints, or Box of ints >= 0 which indicate how many pixels of margin to add around the bounds of the box. The default is 0.
vbox(*components[, spacing=10, margins=...])

Like vertical(), but ensures the width of components matches the container.

Parameters:
  • components – A sequence of Component or spacer objects.
  • spacing (integer >= 0) – How many pixels of inter-element spacing to use
  • margins – An int, tuple of ints, or Box of ints >= 0 which indicate how many pixels of margin to add around the bounds of the box. The default is 0.
align(anchor, *components[, spacing=10])

Aligns a single constraint across multiple components.

Parameters:
  • anchor – The name of a constraint variable that exists on all of the components.
  • components – A sequence of Component objects. Spacers are not allowed.
  • spacing (integer >= 0) – How many pixels of inter-element spacing to use
grid(*rows[, row_align='', row_spacing=10, column_align='', column_spacing=10, margins=...])

Creates an NxM grid of components. Components may span multiple columns or rows.

Parameters:
  • rows – A sequence of sequences of Component objects
  • row_align (string) – The name of a constraint variable on an item. If given, it is used to add constraints on the alignment of items in a row. The constraints will only be applied to items that do not span rows.
  • row_spacing (integer >= 0) – Indicates how many pixels of space should be placed between rows in the grid. The default is 10.
  • column_align (string) – The name of a constraint variable on an item. If given, it is used to add constraints on the alignment of items in a column. The constraints will only be applied to items that do not span columns.
  • column_spacing (integer >= 0) – Indicates how many pixels of space should be placed between columns in the grid. The default is 10.
  • margins – An int, tuple of ints, or Box of ints >= 0 which indicate how many pixels of margin to add around the bounds of the box. The default is 0.

Fine Tuning Layouts

Component defines a Tuple trait layout_size_hint which controls the minimum size of a component when it’s part of a contraints layout. Additionally, Component defines some strength traits that can be used to fine tune the behavior of a component instance during layout. They are:

  • hug_height: How strongly a component prefers the height of its size hint when it could grow.
  • hug_width: How strongly a component prefers the width of its size hint when it could grow.
  • resist_height: How strongly a component resists its height being made smaller than its size hint.
  • resist_width: How strongly a component resists its width being made smaller than its size hint.

The allow values for these strengths are: ‘required’, ‘strong’, ‘medium’, and ‘weak’.