Flow Area Example

An example of the FlowArea widget.

A FlowArea is a very powerful tool for creating a flowing layout of widgets. A FlowArea accepts an arbitrary number of FlowItem children, each of which holds a Container as its content. The layout of these FlowItem children is controlled by the FlowArea attributes:

direction

This is an enum controlling how the items are arranged in the area. Allowable values are ‘left_to_right’, ‘right_to_left’, ‘top_to_bottom’, and ‘bottom_to_top’; and indicate the direction in which items will be added to the area. When the layout space in a given direction is exhausted, the layout will wrap around to the next line. With horizontal directions, lines are stacked top to bottom. With vertical directions, lines are stacked left to right.

align

This is an enum controlling how a layout line is aligned within the layout space. If there is any space leftover after laying out a given line of widgets, that space is distributed according to the value of this enum. Allowable values are ‘leading’, ‘trailing’, ‘center’, and ‘justify’.

horizontal_spacing

This is an int specifying how much horizontal space to place between items or lines in the layout.

vertical_spacing

This is an int specifying how much vertical space to place between items or lines in the layout.

margins

This is a Box of ints specifying how much margin to place on the outside of the layout.

Each FlowItem used in the layout can further customize the behavior:

preferred_size

This is a Size specifying the desired layout size for the item. This size will be used whenever possible, but will not override the minimum or maximum size of the item. If set to (-1, -1) (the default), then the size hint for the item will be used.

align

This is an enum which controls the orthogonal alignment of the item. When an item has neighbors which are larger than itself in the orhthongonal direction, this value controls how the item aligns within that additional space. The valid values area ‘leading’, ‘trailing’, and ‘center’.

stretch

This is an int which controls the amount that the widget should expand to take up additional space in the layout direction. The default is 0 and means that the widget will not expand. When the value is greater than zero, the value is weighted against the stretch factors of the other items in the same line to determine the amount of space given to the item.

ortho_stretch

This is an int which controls the amount that the widget should expand to take up additional space orthogonal to the layout direction. The default is 0 and means that the widget will not expand. If no item in a given line can expand in the ortho direction, then the line will not expand. Otherwise, the stretch factor for a line is equivalent to the maximum of the ortho stretch factors for all items in the line. The extra orthogonal space is then proportioned to the lines weighted on this stretch factor.

The code below creates a flow area populated with several initial flow items. Items can be added and removed, and each individual item is configurable. There is a single item which cannot be removed, and which controls the parameters for the entire area.

Tip

To see this example in action, download it from flow_area and run:

$ enaml-run flow_area.enaml

Screenshot

../_images/ex_flow_area.png

Example Enaml Code

#------------------------------------------------------------------------------
# Copyright (c) 2013, Nucleic Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file LICENSE, distributed with this software.
#------------------------------------------------------------------------------
""" An example of the `FlowArea` widget.

A `FlowArea` is a very powerful tool for creating a flowing layout of
widgets. A `FlowArea` accepts an arbitrary number of `FlowItem` children,
each of which holds a `Container` as its content. The layout of these
`FlowItem` children is controlled by the `FlowArea` attributes:

    `direction`
        This is an enum controlling how the items are arranged in the
        area. Allowable values are 'left_to_right', 'right_to_left',
        'top_to_bottom', and 'bottom_to_top'; and indicate the direction
        in which items will be added to the area. When the layout space
        in a given direction is exhausted, the layout will wrap around
        to the next line. With horizontal directions, lines are stacked
        top to bottom. With vertical directions, lines are stacked
        left to right.

    `align`
        This is an enum controlling how a layout line is aligned within
        the layout space. If there is any space leftover after laying
        out a given line of widgets, that space is distributed according
        to the value of this enum. Allowable values are 'leading',
        'trailing', 'center', and 'justify'.

    `horizontal_spacing`
        This is an int specifying how much horizontal space to place
        between items or lines in the layout.

    `vertical_spacing`
        This is an int specifying how much vertical space to place
        between items or lines in the layout.

    `margins`
        This is a Box of ints specifying how much margin to place
        on the outside of the layout.

Each `FlowItem` used in the layout can further customize the behavior:

    `preferred_size`
        This is a Size specifying the desired layout size for the item.
        This size will be used whenever possible, but will not override
        the minimum or maximum size of the item. If set to (-1, -1)
        (the default), then the size hint for the item will be used.

    `align`
        This is an enum which controls the orthogonal alignment of the
        item. When an item has neighbors which are larger than itself
        in the orhthongonal direction, this value controls how the item
        aligns within that additional space. The valid values area
        'leading', 'trailing', and 'center'.

    `stretch`
        This is an int which controls the amount that the widget should
        expand to take up additional space in the layout direction. The
        default is 0 and means that the widget will not expand. When the
        value is greater than zero, the value is weighted against the
        stretch factors of the other items in the same line to determine
        the amount of space given to the item.

    `ortho_stretch`
        This is an int which controls the amount that the widget should
        expand to take up additional space orthogonal to the layout
        direction. The default is 0 and means that the widget will not
        expand. If no item in a given line can expand in the ortho
        direction, then the line will not expand. Otherwise, the stretch
        factor for a line is equivalent to the maximum of the ortho
        stretch factors for all items in the line. The extra orthogonal
        space is then proportioned to the lines weighted on this stretch
        factor.

The code below creates a flow area populated with several initial flow
items. Items can be added and removed, and each individual item is
configurable. There is a single item which cannot be removed, and which
controls the parameters for the entire area.

<< autodoc-me >>
"""
from enaml.core.api import Include
from enaml.widgets.api import (
    FlowArea, FlowItem, Window, Form, Label, Field, SpinBox, ComboBox,
    Container, Html, GroupBox, Slider, PushButton,
)


enamldef Item(FlowItem):
    align << align_box.selected_item
    stretch << flow_spin.value
    ortho_stretch << ortho_spin.value
    preferred_size << (pref_width.value, pref_height.value)
    GroupBox:
        Form:
            padding = 0
            Label:
                text = 'Preferred Width'
            SpinBox: pref_width:
                minimum = -1
                maximum = 800
                value = -1
            Label:
                text = 'Preferred Height'
            SpinBox: pref_height:
                minimum = -1
                maximum = 800
                value = -1
            Label:
                text = 'Flow Stretch'
            SpinBox: flow_spin:
                minimum = 0
                maximum = 100
                value = 0
            Label:
                text = 'Ortho Stretch'
            SpinBox: ortho_spin:
                minimum = 0
                maximum = 100
                value = 0
            Label:
                text = 'Ortho Align'
            ComboBox: align_box:
                items = ['leading', 'center', 'trailing']
                index = 0
            Html:
                source = '<center>Hello World</center>'


enamldef AreaControls(GroupBox):
    attr area: FlowArea
    event add_item
    event remove_item
    title = 'Area Controls'
    Label:
        text =('Add new items to see how the flow area works.\n'
               'You can also tweak the flow parameters')
    PushButton:
        text = 'Add Item'
        clicked :: add_item()
    PushButton:
        text = 'Remove Item'
        clicked :: remove_item()
    Form:
        padding = 0
        Label:
            text = 'Horizontal Spacing'
        Slider:
            minimum = 0
            maximum = 150
            value := area.horizontal_spacing
        Label:
            text = 'Vertical Spacing'
        Slider:
            minimum = 0
            maximum = 150
            value := area.vertical_spacing
        Label:
            text = 'Direction'
        ComboBox:
            items = [
                'left_to_right', 'right_to_left',
                'top_to_bottom', 'bottom_to_top',
            ]
            index = items.index(area.direction)
            selected_item >> area.direction
        Label:
            text = 'Align'
        ComboBox:
            items = ['leading', 'center', 'justify', 'trailing']
            index = items.index(area.align)
            selected_item >> area.align


enamldef Main(Window):
    initial_size = (800,800)
    Container:
        FlowArea: flow_area:
            FlowItem:
                AreaControls:
                    area = flow_area
                    add_item ::
                        inc.objects.append(Item())
                    remove_item ::
                        if inc.objects:
                            inc.objects.pop()
            Include: inc:
                pass