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
Kiwisolver 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 edgesv_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.
-
hbox(*components[, spacing=10, margins=...])¶ Takes a list of components and lines them up using their left and right edges and ensures that the components’ heights match that of their container.
- Parameters
components – A sequence of
Componentorspacerobjects.spacing (integer >= 0) – How many pixels of inter-element spacing to use
margins – An
int,tupleof ints, orenable.layout.geometry.Boxof 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=...])¶ Takes a list of components and lines them up using their top and bottom edges and ensures that the components’ widths match each other.
- Parameters
components – A sequence of
Componentorspacerobjects.spacing (integer >= 0) – How many pixels of inter-element spacing to use
margins – An
int,tupleof ints, orenable.layout.geometry.Boxof ints >= 0 which indicate how many pixels of margin to add around the bounds of the box. The default is 0.
-
horizontal(*components[, spacing=10])¶ Like
hbox(), but does not ensure that the heights of components match each other.Takes a list of components and lines them up using their left and right edges.
- Parameters
components – A sequence of
Componentorspacerobjects.spacing (integer >= 0) – How many pixels of inter-element spacing to use
-
vertical(*components[, spacing=10])¶ Like
vbox(), but does not ensure that the widths of components match each other.Takes a list of components and lines them up using their top and bottom edges.
- Parameters
components – A sequence of
Componentorspacerobjects.spacing (integer >= 0) – How many pixels of inter-element spacing to use
-
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
Componentobjects. 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
Componentobjectsrow_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,tupleof ints, orenable.layout.geometry.Boxof 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'.
Contrained Layout Pitfalls¶
The
auto_sizetrait ofContaineris completely ignored by constrained layout. Just ignore it.The
boundstrait of aComponentwhich is a child of aConstraintsContaineris not considered when generating a layout. One should instead specify a minimum size withlayout_size_hintand/or add constraints which reference the component’slayout_heightorlayout_widthtraits.Similarly, the
positiontrait of aComponentwhich is a child of aConstraintsContaineris overwritten by the constraint solver and not considered. Add constraints which reference the component’sleftortoptraits if you want to explicitly control the final value ofposition(alsoright,top,v_center, andh_centercan influence the layout position)If a child
Componenthas zerowidthorheightafter the container’srefresh()is called, that usually means the layout is not sufficiently constrained. In that case, you need to add more constraints to the container’slayout_constraints.