# 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

## Example Enaml Code

    #------------------------------------------------------------------------------
    #  Copyright (c) 2013-2025, 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-2024'
            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))