Source code for enable.tools.tool_history_mixin
# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!
""" Defines the ToolHistoryMixin class.
"""
from traits.api import HasTraits, Instance, Int, List
from enable.base_tool import KeySpec
[docs]class ToolHistoryMixin(HasTraits):
""" A mix-in class for tools to maintain a tool state history and to move
backwards and forwards through that history stack.
This mix-in listens for keypressed events; to handle keypresses in a
subclass, call self._history_handle_key(event) to have this mix-in properly
process the event.
"""
# Key to go to the original or start state in the history.
reset_state_key = Instance(KeySpec, args=("Esc",))
# Key to go to the previous state in the history.
prev_state_key = Instance(KeySpec, args=("Left", "control"))
# Key to go to the next state in the history.
next_state_key = Instance(KeySpec, args=("Right", "control"))
# The state stack.
_history = List
# The current index into _history
_history_index = Int
# ------------------------------------------------------------------------
# Abstract methods that subclasses must implement to handle keypresses
# ------------------------------------------------------------------------
def _next_state_pressed(self):
""" Called when the tool needs to advance to the next state in the
stack.
The **_history_index** will have already been set to the index
corresponding to the next state.
"""
pass
def _prev_state_pressed(self):
""" Called when the tool needs to advance to the previous state in the
stack.
The **_history_index** will have already been set to the index
corresponding to the previous state.
"""
pass
def _reset_state_pressed(self):
""" Called when the tool needs to reset its history.
The history index will have already been set to 0.
"""
pass
# ------------------------------------------------------------------------
# Protected methods for subclasses to use
# ------------------------------------------------------------------------
def _current_state(self):
""" Returns the current history state.
"""
return self._history[self._history_index]
def _reset_state(self, state):
""" Clears the history stack and sets the first or original state in
the history to *state*.
"""
self._history = [state]
self._history_index = 0
return
def _append_state(self, state, set_index=True):
""" Clears the history after the current **_history_index**, and
appends the given state to the history.
If *set_index* is True, the method sets the **_history_index** to
match the new, truncated history. If it is False, the history index
is unchanged.
"""
new_history = self._history[: self._history_index + 1] + [state]
self._history = new_history
if set_index:
self._history_index = len(self._history) - 1
return
def _pop_state(self):
""" Pops the most last state off the history stack.
If the history index points to the end of the stack, then it is
adjusted; otherwise, the index is unaffected. If the stack is empty,
the method raises an IndexError.
Returns the popped state.
"""
if len(self._history) == 0:
raise IndexError("Unable to pop empty history stack.")
if self._history_index == len(self._history) - 1:
self._history_index -= 1
return self._history.pop()
# ------------------------------------------------------------------------
# Private methods / event handlers
# ------------------------------------------------------------------------
[docs] def normal_key_pressed(self, event):
""" Handles a key being pressed, and takes appropriate action if it is
one of the history keys defined for this class.
"""
self._history_handle_key(event)
return
def _history_handle_key(self, event):
if self.reset_state_key.match(event):
self._history_index = 0
self._reset_state_pressed()
event.handled = True
elif self.prev_state_key.match(event):
if self._history_index > 0:
self._history_index -= 1
self._prev_state_pressed()
event.handled = True
elif self.next_state_key.match(event):
if self._history_index <= len(self._history) - 2:
self._history_index += 1
self._next_state_pressed()
event.handled = True
else:
return