#!/usr/bin/env python
#
# togglepanel.py - A panel which contains a button, and some content.
# Pushing the button toggles the visibility of the content.
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
"""This module provides the :class:`TogglePanel` class, which is a panel that
contains a button and some content. Pushing the button toggles the visibility
of the content.
"""
import warnings
import wx
import wx.lib.newevent as wxevent
[docs]
class TogglePanel(wx.Panel):
    """A :class:`TogglePanel` is a ``wx.Panel`` that contains a button and
    some content.
    Pushing the button toggles the visibility of the content.
    All of the content should be added to the panel which is returned by the
    :meth:`GetPane` method.
    The ``TogglePanel`` may be used in place of the ``wx.CollapsiblePane``,
    which is buggy under Linux/GTK.
    """
    def __init__(self,
                 parent,
                 toggleSide=wx.TOP,
                 initialState=True,
                 label=None):
        """Create a :class:`TogglePanel`.
        :arg parent:       The :mod:`wx` parent object.
        :arg toggleSide:   Which side to place the toggle button. Must be one
                           of :attr:`wx.TOP`, :attr:`wx.BOTTOM`,
                           :attr:`wx.LEFT`, or :attr:`wx.RIGHT`.
        :arg initialState: Initial state for the panel content - visible
                           (``True``) or hidden (``False``).
        :arg label:        A label to be displayed on the toggle button.
        """
        wx.Panel.__init__(self, parent)
        self.__contentPanel = wx.Panel(self)
        if toggleSide in (wx.TOP, wx.BOTTOM):
            self.__mainSizer   = wx.BoxSizer(wx.VERTICAL)
            self.__toggleSizer = wx.BoxSizer(wx.HORIZONTAL)
        elif toggleSide in (wx.LEFT, wx.RIGHT):
            self.__mainSizer   = wx.BoxSizer(wx.HORIZONTAL)
            self.__toggleSizer = wx.BoxSizer(wx.VERTICAL)
        else:
            raise ValueError('toggleSide must be one of wx.TOP, '
                             'wx.BOTTOM, wx.LEFT or wx.RIGHT')
        self.__toggleButton = wx.StaticText(
            self,
            style=(wx.ALIGN_CENTRE_VERTICAL | wx.ALIGN_CENTRE_HORIZONTAL))
        if   toggleSide == wx.TOP:    hideLabel = '\u25B2'
        elif toggleSide == wx.BOTTOM: hideLabel = '\u25BC'
        elif toggleSide == wx.LEFT:   hideLabel = '\u25C0'
        elif toggleSide == wx.RIGHT:  hideLabel = '\u25B6'
        if   toggleSide == wx.TOP:    showLabel = '\u25BC'
        elif toggleSide == wx.BOTTOM: showLabel = '\u25B2'
        elif toggleSide == wx.LEFT:   showLabel = '\u25B6'
        elif toggleSide == wx.RIGHT:  showLabel = '\u25C0'
        self.__showLabel = showLabel
        self.__hideLabel = hideLabel
        self.__label     = label
        self.__state     = not initialState
        self.__toggleSizer.Add(self.__toggleButton, flag=wx.EXPAND)
        self.__toggleSizer.Add((1, 1), flag=wx.EXPAND, proportion=1)
        if toggleSide in (wx.TOP, wx.LEFT):
            self.__mainSizer.Add(self.__toggleSizer,
                                 flag=wx.EXPAND | wx.ALL,
                                 border=2)
            self.__mainSizer.Add(self.__contentPanel,
                                 flag=wx.EXPAND,
                                 proportion=1)
        elif toggleSide in (wx.BOTTOM, wx.RIGHT):
            self.__mainSizer.Add(self.__contentPanel,
                                 flag=wx.EXPAND,
                                 proportion=1)
            self.__mainSizer.Add(self.__toggleSizer,
                                 flag=wx.EXPAND | wx.ALL,
                                 border=2)
        self.__toggleButton.Bind(wx.EVT_LEFT_DOWN, self.Toggle)
        self.SetSizer(self.__mainSizer)
        self.Expand(initialState)
    @property
    def button(self):
        """Returns the toggle button (actually a ``wx.StaticText``). This
        is for testing purposes.
        """
        return self.__toggleButton
    def __refresh(self):
        """Refreshes the layout. """
        self.Layout()
        self.__contentPanel.Layout()
    def __expand(self, expand=True, force=False):
        """Expands or collapses the content panel.
        :arg expand: ``True`` to expand, ``False`` to collapse.
        :arg force:  If ``True``, the panel is re-configured, regardless of
                     whether it is already in the requested expanded/collapsed
                     state.
        """
        if not force and self.IsExpanded() == expand:
            return
        self.__mainSizer.Show(self.__contentPanel, expand)
        if expand: label = self.__hideLabel
        else:      label = self.__showLabel
        if self.__label is not None:
            label = '{} {}'.format(label, self.__label)
        self.__state = expand
        self.__toggleButton.SetLabel(label)
        self.__refresh()
[docs]
    def SetLabel(self, label):
        """Sets the label to show on the toggle button. Pass in ``None``
        for no label.
        """
        self.__label = label
        self.__expand(self.IsExpanded(), force=True) 
[docs]
    def GetLabel(self):
        """Returns the current toggle button label """
        return self.__label 
[docs]
    def Expand(self, expand=True):
        """Expand the content pane.
        :arg expand: ``True`` to expand, ``False`` to collapse.
        """
        self.__expand(expand) 
[docs]
    def Collapse(self):
        """Collapse the content pane. """
        self.Expand(False) 
[docs]
    def IsExpanded(self):
        """Return ``True`` if the content pane is currently expanded,
        ``False`` otherwise.
        """
        return self.__state 
[docs]
    def Toggle(self, ev=None):
        """Toggles visibility of the panel content.
        This method is called when the button is pushed. If ``ev`` is not
        ``None``, an ``EVT_TOGGLEPANEL_EVENT`` is generated.
        """
        newState = not self.IsExpanded()
        self.Expand(newState)
        if ev is not None:
            ev = TogglePanelEvent(newState=newState)
            ev.SetEventObject(self)
            wx.PostEvent(self, ev) 
[docs]
    def GetPane(self):
        """Returns the :class:`wx.Panel` to which all content should be
        added.
        """
        return self.__contentPanel 
 
_TogglePanelEvent, _EVT_TOGGLEPANEL_EVENT = wxevent.NewEvent()
EVT_TOGGLEPANEL_EVENT = _EVT_TOGGLEPANEL_EVENT
"""Identifier for the :data:`TogglePanelEvent` event."""
TogglePanelEvent = _TogglePanelEvent
"""Event emitted when the toggle button is pushed. Contains the
following attributes:
  - ``newState``: The new visibility state of the toggle panel - ``True``
                  corresponds to visible, ``False`` to invisible.
"""