Skip to content

Commit

Permalink
Some refactoring and generalizing, based on writing docs.
Browse files Browse the repository at this point in the history
  • Loading branch information
corranwebster committed Jan 19, 2015
1 parent 4901c48 commit 5c71cfc
Show file tree
Hide file tree
Showing 9 changed files with 116 additions and 34 deletions.
48 changes: 32 additions & 16 deletions enable/tests/tools/apptools/commands_test_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ def setUp(self):
data=(25, 25, 150, 150),
previous_rectangle=(50, 50, 100, 100))

def test_name_default(self):
self.assertEqual(self.command.name, 'Resize Component')

def test_name_alternate_component_name(self):
self.command.component_name = 'My Component'
self.assertEqual(self.command.name, 'Resize My Component')

def test_do(self):
command = self.command

Expand Down Expand Up @@ -98,6 +105,7 @@ def test_redo(self):

def test_merge(self):
command = self.command
command.mergeable = True
other_command = ResizeCommand(component=self.component,
data=(0, 0, 200, 200),
previous_rectangle=(50, 50, 100, 100))
Expand All @@ -107,12 +115,13 @@ def test_merge(self):

self.assertTrue(merged)
self.assertEqual(command.data, (0, 0, 200, 200))
self.assertFalse(command.final)
self.assertFalse(command.mergeable)

def test_merge_other_final(self):
def test_merge_other_mergeable(self):
command = self.command
command.mergeable = True
other_command = ResizeCommand(component=self.component,
final=True,
mergeable=True,
data=(0, 0, 200, 200),
previous_rectangle=(50, 50, 100, 100))

Expand All @@ -121,11 +130,10 @@ def test_merge_other_final(self):

self.assertTrue(merged)
self.assertEqual(command.data, (0, 0, 200, 200))
self.assertTrue(command.final)
self.assertTrue(command.mergeable)

def test_merge_final(self):
def test_merge_unmergeable(self):
command = self.command
command.final = True
other_command = ResizeCommand(component=self.component,
data=(0, 0, 200, 200),
previous_rectangle=(50, 50, 100, 100))
Expand All @@ -138,7 +146,7 @@ def test_merge_final(self):

def test_merge_wrong_component(self):
command = self.command
command.final = True
command.mergeable = True
other_component = Component()
other_command = ResizeCommand(component=other_component,
data=(0, 0, 200, 200),
Expand All @@ -152,7 +160,7 @@ def test_merge_wrong_component(self):

def test_merge_wrong_class(self):
command = self.command
command.final = True
command.mergeable = True
other_command = ComponentCommand(component=self.component)

with self.assertTraitDoesNotChange(command, 'data'):
Expand All @@ -170,6 +178,13 @@ def setUp(self):
data=(25, 25),
previous_position=(50, 50))

def test_name_default(self):
self.assertEqual(self.command.name, 'Move Component')

def test_name_alternate_component_name(self):
self.command.component_name = 'My Component'
self.assertEqual(self.command.name, 'Move My Component')

def test_do(self):
command = self.command

Expand Down Expand Up @@ -214,6 +229,7 @@ def test_redo(self):

def test_merge(self):
command = self.command
command.mergeable = True
other_command = MovePositionCommand(component=self.component,
data=(0, 0),
previous_position=(50, 50))
Expand All @@ -223,24 +239,24 @@ def test_merge(self):

self.assertTrue(merged)
self.assertEqual(command.data, (0, 0))
self.assertFalse(command.final)
self.assertFalse(command.mergeable)

def test_merge_other_final(self):
def test_merge_other_mergeable(self):
command = self.command
command.mergeable = True
other_command = MovePositionCommand(component=self.component,
final=True, data=(0, 0),
mergeable=True, data=(0, 0),
previous_position=(50, 50))

with self.assertTraitChanges(command, 'data'):
merged = command.merge(other_command)

self.assertTrue(merged)
self.assertEqual(command.data, (0, 0))
self.assertTrue(command.final)
self.assertTrue(command.mergeable)

def test_merge_final(self):
def test_merge_unmergeable(self):
command = self.command
command.final = True
other_command = MovePositionCommand(component=self.component,
data=(0, 0),
previous_position=(50, 50))
Expand All @@ -253,7 +269,7 @@ def test_merge_final(self):

def test_merge_wrong_component(self):
command = self.command
command.final = True
command.mergeable = True
other_component = Component()
other_command = MovePositionCommand(component=other_component,
data=(0, 0),
Expand All @@ -268,7 +284,7 @@ def test_merge_wrong_component(self):

def test_merge_wrong_class(self):
command = self.command
command.final = True
command.mergeable = True
other_command = ComponentCommand(component=self.component)

with self.assertTraitDoesNotChange(command, 'data'):
Expand Down
2 changes: 1 addition & 1 deletion enable/tests/tools/apptools/move_command_tool_test_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def test_drag_component(self):
# check that the ResizeCommand has right parameters
self.assertEqual(command.data, (100, 0))
self.assertEqual(command.previous_position, (50, 50))
self.assertTrue(command.final)
self.assertFalse(command.mergeable)
self.assertEqual(command.component, self.component)

def test_drag_cancel(self):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ def test_drag_component(self):
# check that the ResizeCommand has right parameters
self.assertEqual(command.data, (50, 50, 150, 50))
self.assertEqual(command.previous_rectangle, (50, 50, 100, 100))
self.assertTrue(command.final)
self.assertFalse(command.mergeable)
self.assertEqual(command.component, self.component)

def test_drag_cancel(self):
Expand Down
28 changes: 28 additions & 0 deletions enable/tools/apptools/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#
# (C) Copyright 2015 Enthought, Inc., Austin, TX
# All right reserved.
#
# This file is open source software distributed according to the terms in
# LICENSE.txt
#

"""
Enable Apptools Integration
===========================
Apptools (https://github.com/enthought/apptools) is a library of useful code
for building GUI applications. It includes code for features like preferences,
undo/redo support, and seelction management.
The code in this sub-package helps applications interface with the
functionality provided by Apptools, but is optional from the point of view
of the Enable codebase as a whole.
"""

# Support for Undo/Redo with Enable
from .commands import ComponentCommand, ResizeCommand, MovePositionCommand
from .command_tool import BaseCommandTool, BaseUndoTool
from .move_command_tool import MoveCommandTool
from .resize_command_tool import ResizeCommandTool
from .undo_tool import UndoTool
5 changes: 4 additions & 1 deletion enable/tools/apptools/command_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
unicode_literals)

from apptools.undo.api import ICommandStack, IUndoManager
from traits.api import Instance
from traits.api import Callable, Instance

from enable.base_tool import BaseTool

Expand All @@ -32,6 +32,9 @@ class BaseCommandTool(BaseTool):
"""

# The command that the tool creates in response to user action.
command = Callable

# The command stack to push to.
command_stack = Instance(ICommandStack)

Expand Down
16 changes: 8 additions & 8 deletions enable/tools/apptools/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,15 @@ class ResizeCommand(ComponentCommand):
#: The old rectangle of the component as a tuple (x, y, width, height).
previous_rectangle = Tuple

#: whether the resize is finished, or if additional resizes can be merged.
final = Bool
#: whether additional resizes can be merged or if the resize is finished.
mergeable = Bool

#-------------------------------------------------------------------------
# AbstractCommand interface
#-------------------------------------------------------------------------

def merge(self, other):
if not self.final and isinstance(other, self.__class__) and \
if self.mergeable and isinstance(other, self.__class__) and \
other.component == self.component:
return self._merge_data(other)
return super(ResizeCommand, self).merge(other)
Expand Down Expand Up @@ -91,7 +91,7 @@ def _change_rectangle(self, rectangle):

def _merge_data(self, other):
self.data = other.data
self.final = other.final
self.mergeable = other.mergeable
return True

#-------------------------------------------------------------------------
Expand All @@ -112,15 +112,15 @@ class MoveCommand(ComponentCommand):
"""

#: whether the move is finished, or if additional moves can be merged.
final = Bool
#: whether additional moves can be merged or if the move is finished.
mergeable = Bool

#-------------------------------------------------------------------------
# AbstractCommand interface
#-------------------------------------------------------------------------

def merge(self, other):
if not self.final and isinstance(other, self.__class__) and \
if self.mergeable and isinstance(other, self.__class__) and \
other.component == self.component:
return self._merge_data(other)
return super(MoveCommand, self).merge(other)
Expand Down Expand Up @@ -213,5 +213,5 @@ def undo(self):

def _merge_data(self, other):
self.data = other.data
self.final = other.final
self.mergeable = other.mergeable
return True
24 changes: 21 additions & 3 deletions enable/tools/apptools/move_command_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from __future__ import (division, absolute_import, print_function,
unicode_literals)

from traits.api import Tuple
from traits.api import Bool, Tuple

from enable.tools.move_tool import MoveTool

Expand All @@ -34,9 +34,20 @@ class MoveCommandTool(MoveTool, BaseCommandTool):
"""

#-------------------------------------------------------------------------
# 'MoveCommandTool' interface
#-------------------------------------------------------------------------

#: Whether or not subsequent moves can be merged with this one.
mergeable = Bool

#: The initial component position.
_initial_position = Tuple(0, 0)

#-------------------------------------------------------------------------
# 'DragTool' interface
#-------------------------------------------------------------------------

def drag_start(self, event):
if self.component:
# we need to save the initial position to give to the Command
Expand All @@ -46,11 +57,11 @@ def drag_start(self, event):
def drag_end(self, event):
""" End the drag operation, issuing a MovePositionCommand """
if self.component:
command = MovePositionCommand(
command = self.command(
component=self.component,
data=tuple(self.component.position),
previous_position=self._initial_position,
final=True)
mergeable=self.mergeable)
self.command_stack.push(command)
event.handled = True
return
Expand All @@ -68,3 +79,10 @@ def drag_cancel(self, event):

event.handled = True
return

#-------------------------------------------------------------------------
# Trait handlers
#-------------------------------------------------------------------------

def _command_default(self):
return MovePositionCommand
22 changes: 20 additions & 2 deletions enable/tools/apptools/resize_command_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from __future__ import (division, absolute_import, print_function,
unicode_literals)

from traits.api import Tuple
from traits.api import Bool, Tuple

from enable.tools.resize_tool import ResizeTool

Expand All @@ -34,9 +34,20 @@ class ResizeCommandTool(ResizeTool, BaseCommandTool):
"""

#-------------------------------------------------------------------------
# 'ResizeCommandTool' interface
#-------------------------------------------------------------------------

#: Whether or not subsequent moves can be merged with this one.
mergeable = Bool

#: The initial component position.
_initial_rectangle = Tuple(0, 0, 0, 0)

#-------------------------------------------------------------------------
# 'DragTool' interface
#-------------------------------------------------------------------------

def drag_start(self, event):
if self.component is not None:
# we need to save the initial position to give to the Command
Expand All @@ -49,7 +60,7 @@ def drag_start(self, event):
def drag_end(self, event):
""" End the drag operation, issuing a MovePositionCommand """
if self.component is not None:
command = ResizeCommand(
command = self.command(
component=self.component,
data=tuple(self.component.position + self.component.bounds),
previous_rectangle=self._initial_rectangle,
Expand All @@ -71,3 +82,10 @@ def drag_cancel(self, event):
self.component.position = list(self._initial_rectangle)
event.handled = True
return True

#-------------------------------------------------------------------------
# Trait handlers
#-------------------------------------------------------------------------

def _command_default(self):
return ResizeCommand
3 changes: 1 addition & 2 deletions examples/enable/tools/apptools/undoable_move_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@
from enable.api import Container, Window, KeySpec
from enable.example_application import DemoApplication, demo_main
from enable.primitives.api import Box
from enable.tools.apptools.move_command_tool import MoveCommandTool
from enable.tools.apptools.undo_tool import UndoTool
from enable.tools.apptools.api import MoveCommandTool, UndoTool


class UndoableMoveApplication(DemoApplication):
Expand Down

0 comments on commit 5c71cfc

Please sign in to comment.