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
Component
orspacer
objects.spacing (integer >= 0) – How many pixels of inter-element spacing to use
margins – An
int
,tuple
of ints, orenable.layout.geometry.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=...])¶ 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
Component
orspacer
objects.spacing (integer >= 0) – How many pixels of inter-element spacing to use
margins – An
int
,tuple
of ints, orenable.layout.geometry.Box
of 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
Component
orspacer
objects.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
Component
orspacer
objects.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
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
objectsrow_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, orenable.layout.geometry.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'
.
Contrained Layout Pitfalls¶
The
auto_size
trait ofContainer
is completely ignored by constrained layout. Just ignore it.The
bounds
trait of aComponent
which is a child of aConstraintsContainer
is not considered when generating a layout. One should instead specify a minimum size withlayout_size_hint
and/or add constraints which reference the component’slayout_height
orlayout_width
traits.Similarly, the
position
trait of aComponent
which is a child of aConstraintsContainer
is overwritten by the constraint solver and not considered. Add constraints which reference the component’sleft
ortop
traits if you want to explicitly control the final value ofposition
(alsoright
,top
,v_center
, andh_center
can influence the layout position)If a child
Component
has zerowidth
orheight
after 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
.