Dock Area Example

This example demonstrates the use of the advanced DockArea widget.

The DockArea widget provides a canvas into which DockItems can be docked and undocked at will. The layout configuration of the area can be saved and restored using a layout object which can be easily pickled.

Tip

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

$ enaml-run dock_area.enaml

Screenshot

../_images/ex_dock_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.
#------------------------------------------------------------------------------
""" This example demonstrates the use of the advanced DockArea widget.

The DockArea widget provides a canvas into which DockItems can be docked
and undocked at will. The layout configuration of the area can be saved
and restored using a layout object which can be easily pickled.

<< autodoc-me >>
"""
import pickle

from atom.api import Atom, ContainerList, Str

from enaml.layout.api import (
    HSplitLayout, VSplitLayout, TabLayout, InsertItem, hbox, vbox, spacer
)
from enaml.stdlib.dock_area_styles import available_styles
from enaml.stdlib.message_box import question
from enaml.widgets.api import (
    Window, Container, DockArea, DockItem, PushButton, Field, Html, Slider,
    ObjectCombo, CheckBox, MultilineField
)


def cap_case(name):
    return ' '.join(s.capitalize() for s in name.split('-'))


def confirm_close(window, event):
    button = question(
        window, 'Dock item closing', 'Are you sure you want to close this dock item?'
    )
    if button and button.action == 'accept':
        event.accept()
    else:
        event.ignore()


class LineCollector(Atom):
    """ A simple class used to generate event logging text.

    """
    #: The header to use as the first lines of the text.
    HEADER = '#   Item     Event Type            \n'\
             '-----------------------------------'

    #: The output text of the collector; updated when data changes.
    text = Str(HEADER)

    #: The data for the collector. This is a list of 2-tuples of
    #: the form (str, DockItemEvent.Type).
    data = ContainerList()

    def _observe_data(self, change):
        self.update_text()

    def update_text(self):
        """ Regenerate the output text from the current data.

        """
        parts = []
        count = len(self.data)
        for index, (evt_type, name) in enumerate(reversed(self.data)):
            parts.append((count - index, evt_type, name))
        lines = [self.HEADER]
        for item in parts:
            num, name, enum = item
            line = '{0!s: <4}{1: <9}{2: <19}'.format(num, name, enum.name)
            lines.append(line)
        self.text = '\n'.join(lines)


enamldef MyDockArea(DockArea):
    layout = HSplitLayout(
        VSplitLayout(
            HSplitLayout(
                VSplitLayout('item_1', 'item_3'),
                'item_4',
            ),
            'logger',
        ),
        VSplitLayout(
            TabLayout('item_6', 'item_7', 'item_8', 'item_9'),
            'item_5',
            'item_2',
        ),
    )
    DockItem:
        name = 'item_1'
        title = 'Item 1'
        Container:
            Field: pass
            Field: pass
            Field: pass
        closing ::
            confirm_close(self, change['value'])
    DockItem:
        name = 'item_2'
        title = 'Item 2'
        Container:
            PushButton: text = 'foo'
            PushButton: text = 'bar'
            PushButton: text = 'baz'
    DockItem:
        name = 'item_3'
        title = 'Item 3'
        Container:
            Html: source = '<h1><center>Hello World!</center></h1>'
    DockItem:
        name = 'item_4'
        title = 'Item 4'
        Container:
            Html: source = '<h1><center>Hello Enaml!</center></h1>'
    DockItem:
        name = 'item_5'
        title = 'Item 5'
        Container:
            Slider: pass
            Slider: pass
            Slider: pass
    DockItem:
        name = 'item_6'
        title = 'Item 6'
        Container:
            Html: source = '<h1><center>Guten Tag!</center></h1>'
    DockItem:
        name = 'item_7'
        title = 'Item 7'
        Container:
            Field: pass
            Field: pass
            Field: pass
            Field: pass
    DockItem:
        name = 'item_8'
        title = 'Item 8'
        Container:
            PushButton: text = 'spam'
            PushButton: text = 'ham'
            PushButton: text = 'green'
            PushButton: text = 'eggs'
    DockItem:
        name = 'item_9'
        title = 'Item 9'
        Container:
            Html: source = '<h1><center>Bonjour!</center></h1>'
    DockItem:
        name = 'logger'
        title = 'Logger'
        closable = False
        Container:
            MultilineField:
                attr collector = LineCollector()
                name = 'line-collector'
                text << collector.text
                font = '9pt Courier'
                read_only = True


enamldef MyItem(DockItem): owner:
    Container:
        Field: text = owner.name
        Field: text = owner.name
        Field: text = owner.name
        Field: text = owner.name


enamldef Main(Window):
    Container:
        attr stored = None
        padding = (0, 0, 0, 10)
        constraints = [
            hbox(
                vbox(10, save_b, restore_b, add_b, style_c, c_box, spacer),
                area,
            )
        ]
        PushButton: save_b:
            text = 'Save Layout'
            clicked ::
                layout = area.save_layout()
                parent.stored = pickle.dumps(layout, -1)
        PushButton: restore_b:
            text = 'Restore Layout'
            enabled << stored is not None
            clicked ::
                layout = pickle.loads(stored)
                with area.suppress_dock_events():
                    area.apply_layout(layout)
        PushButton: add_b:
            text = 'Add Items'
            clicked ::
                for _ in range(3):
                    n = (len(area.children) + 1)
                    name = 'item_%d' % n
                    title = 'Item %d' % n
                    item = MyItem(area, name=name, title=title)
                    op = InsertItem(item=name, target='item_1')
                    area.update_layout(op)
        ObjectCombo: style_c:
            items = available_styles()
            to_string = cap_case
            selected = 'vs-2010'
        CheckBox: c_box:
            text = 'Enable Dock Events'
            checked := area.dock_events_enabled
        MyDockArea: area:
            style << style_c.selected
            dock_event ::
                event = change['value']
                field = area.find('line-collector')
                field.collector.data.append((event.name, event.type))