Source code for enaml.widgets.dock_area

# 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.
from contextlib import contextmanager

from atom.api import (
    Bool, Coerced, Enum, Typed, ForwardTyped, Str, Event, set_default

from enaml.core.declarative import d_, observe
from enaml.layout.dock_layout import DockLayout, DockLayoutOp
from enaml.styling import StyleSheet

from .constraints_widget import ConstraintsWidget, ProxyConstraintsWidget
from .dock_events import DockEvent
from .dock_item import DockItem

_dock_area_styles = None
def get_registered_styles(name):
    # lazy import the stdlib module in case it's never needed.
    global _dock_area_styles
    if _dock_area_styles is None:
        import enaml
        with enaml.imports():
            from enaml.stdlib import dock_area_styles
        _dock_area_styles = dock_area_styles
    return _dock_area_styles.get_registered_styles(name)

class ProxyDockArea(ProxyConstraintsWidget):
    """ The abstract definition of a proxy DockArea object.

    #: A reference to the Stack declaration.
    declaration = ForwardTyped(lambda: DockArea)

    def set_tab_position(self, position):
        raise NotImplementedError

    def set_live_drag(self, live_drag):
        raise NotImplementedError

    def set_style(self, style):
        raise NotImplementedError

    def set_dock_events_enabled(self, enabled):
        raise NotImplementedError

    def save_layout(self):
        raise NotImplementedError

    def apply_layout(self, layout):
        raise NotImplementedError

    def update_layout(self, ops):
        raise NotImplementedError

[docs] class DockArea(ConstraintsWidget): """ A component which arranges dock item children. """ #: The layout of dock items for the area. This attribute is *not* #: kept in sync with the layout state of the widget at runtime. The #: 'save_layout' method should be called to retrieve the current #: layout state. layout = d_(Coerced(DockLayout, ())) #: The default tab position for newly created dock tabs. tab_position = d_(Enum('top', 'bottom', 'left', 'right')) #: Whether the dock items resize as a dock splitter is being dragged #: (True), or if a simple indicator is drawn until the drag handle #: is released (False). The default is True. live_drag = d_(Bool(True)) #: The name of the registered style to apply to the dock area. The #: list of available styles can be retrieved by calling the function #: `available_styles` in the `enaml.stdlib.dock_area_styles` module. #: The default is a style inspired by Visual Studio 2010 #: #: Users can also define and use their own custom style sheets with #: the dock area. Simply set this attribute to an empty string so #: the default styling is disabled, and proceed to use style sheets #: as with any other widget (see the stdlib styles for inspiration). #: #: Only one mode of styling should be used for the dock area at a #: time. Using both modes simultaneously is undefined. style = d_(Str('vs-2010')) #: Whether or not dock events are enabled for the area. dock_events_enabled = d_(Bool(False)) #: An event emitted when a dock event occurs in the dock area. #: `dock_events_enabled` must be True in order to recieve events. dock_event = d_(Event(DockEvent), writable=False) #: A Stack expands freely in height and width by default hug_width = set_default('ignore') hug_height = set_default('ignore') #: A reference to the ProxyStack widget. proxy = Typed(ProxyDockArea) #: The style sheet created from the 'style' attribute. _internal_style = Typed(StyleSheet)
[docs] def initialize(self): """ A reimplemented initializer method. This method ensures the internal style sheet is created. """ super(DockArea, self).initialize() self._refresh_internal_style()
[docs] def dock_items(self): """ Get the dock items defined on the stack """ return [c for c in self.children if isinstance(c, DockItem)]
[docs] def save_layout(self): """ Save the current layout state of the dock area. Returns ------- result : docklayout The current layout state of the dock area. """ if self.proxy_is_active: return self.proxy.save_layout() return self.layout
[docs] def apply_layout(self, layout): """ Apply a new layout to the dock area. Parameters ---------- layout : DockLayout The dock layout to apply to the dock area. """ assert isinstance(layout, DockLayout), 'layout must be a DockLayout' if self.proxy_is_active: return self.proxy.apply_layout(layout)
[docs] def update_layout(self, ops): """ Update the layout configuration using layout operations. Parameters ---------- ops : DockLayoutOp or iterable A single DockLayoutOp instance or an iterable of the same. The operations will be executed in order. If a given op is not valid for the current layout state, it will be skipped. """ if isinstance(ops, DockLayoutOp): ops = [ops] for op in ops: assert isinstance(op, DockLayoutOp) if self.proxy_is_active: self.proxy.update_layout(ops)
[docs] @contextmanager def suppress_dock_events(self): """ A context manager which supresses dock events. This manager will disable dock events for the duration of the context, and restore the old value upon exit. """ enabled = self.dock_events_enabled self.dock_events_enabled = False yield self.dock_events_enabled = enabled
#-------------------------------------------------------------------------- # Observers #-------------------------------------------------------------------------- @observe('style') def _update_style(self, change): """ An observer which updates the internal style sheet. """ change_t = change['type'] if change_t == 'update' or change_t == 'delete': self._refresh_internal_style() @observe('layout') def _update_layout(self, change): """ An observer which updates the layout when it changes. """ if change['type'] == 'update': self.apply_layout(change['value']) @observe('tab_position', 'live_drag', 'style', 'dock_events_enabled') def _update_proxy(self, change): """ Update the proxy when the area state changes. """ # The superclass implementation is sufficient. super(DockArea, self)._update_proxy(change) #-------------------------------------------------------------------------- # Private API #-------------------------------------------------------------------------- def _refresh_internal_style(self): old = self._internal_style if old is not None: old.destroy() self._internal_style = None if style_t = get_registered_styles( if style_t is not None: sheet = StyleSheet() style_t()(sheet) self._internal_style = sheet sheet.set_parent(self)