From 5c71cfcfcfbce84dc00c687cd06abd351aa1daef Mon Sep 17 00:00:00 2001 From: Corran Webster Date: Mon, 19 Jan 2015 12:01:50 +0000 Subject: [PATCH] Some refactoring and generalizing, based on writing docs. --- .../tools/apptools/commands_test_case.py | 48 ++++++++++++------- .../apptools/move_command_tool_test_case.py | 2 +- .../apptools/resize_command_tool_test_case.py | 2 +- enable/tools/apptools/api.py | 28 +++++++++++ enable/tools/apptools/command_tool.py | 5 +- enable/tools/apptools/commands.py | 16 +++---- enable/tools/apptools/move_command_tool.py | 24 ++++++++-- enable/tools/apptools/resize_command_tool.py | 22 ++++++++- .../tools/apptools/undoable_move_tool.py | 3 +- 9 files changed, 116 insertions(+), 34 deletions(-) create mode 100644 enable/tools/apptools/api.py diff --git a/enable/tests/tools/apptools/commands_test_case.py b/enable/tests/tools/apptools/commands_test_case.py index 662ee3472..5c6d4835c 100644 --- a/enable/tests/tools/apptools/commands_test_case.py +++ b/enable/tests/tools/apptools/commands_test_case.py @@ -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 @@ -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)) @@ -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)) @@ -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)) @@ -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), @@ -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'): @@ -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 @@ -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)) @@ -223,12 +239,13 @@ 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'): @@ -236,11 +253,10 @@ def test_merge_other_final(self): 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)) @@ -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), @@ -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'): diff --git a/enable/tests/tools/apptools/move_command_tool_test_case.py b/enable/tests/tools/apptools/move_command_tool_test_case.py index 801691f70..8afe2c5a6 100644 --- a/enable/tests/tools/apptools/move_command_tool_test_case.py +++ b/enable/tests/tools/apptools/move_command_tool_test_case.py @@ -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): diff --git a/enable/tests/tools/apptools/resize_command_tool_test_case.py b/enable/tests/tools/apptools/resize_command_tool_test_case.py index febef498f..2472f8591 100644 --- a/enable/tests/tools/apptools/resize_command_tool_test_case.py +++ b/enable/tests/tools/apptools/resize_command_tool_test_case.py @@ -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): diff --git a/enable/tools/apptools/api.py b/enable/tools/apptools/api.py new file mode 100644 index 000000000..e506d1271 --- /dev/null +++ b/enable/tools/apptools/api.py @@ -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 diff --git a/enable/tools/apptools/command_tool.py b/enable/tools/apptools/command_tool.py index 0c88273be..783d32e0d 100644 --- a/enable/tools/apptools/command_tool.py +++ b/enable/tools/apptools/command_tool.py @@ -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 @@ -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) diff --git a/enable/tools/apptools/commands.py b/enable/tools/apptools/commands.py index 83b9c6f4a..522bb45eb 100644 --- a/enable/tools/apptools/commands.py +++ b/enable/tools/apptools/commands.py @@ -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) @@ -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 #------------------------------------------------------------------------- @@ -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) @@ -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 diff --git a/enable/tools/apptools/move_command_tool.py b/enable/tools/apptools/move_command_tool.py index 7e84e4189..74a9a84c4 100644 --- a/enable/tools/apptools/move_command_tool.py +++ b/enable/tools/apptools/move_command_tool.py @@ -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 @@ -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 @@ -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 @@ -68,3 +79,10 @@ def drag_cancel(self, event): event.handled = True return + + #------------------------------------------------------------------------- + # Trait handlers + #------------------------------------------------------------------------- + + def _command_default(self): + return MovePositionCommand diff --git a/enable/tools/apptools/resize_command_tool.py b/enable/tools/apptools/resize_command_tool.py index cca81d239..f5c1a36e1 100644 --- a/enable/tools/apptools/resize_command_tool.py +++ b/enable/tools/apptools/resize_command_tool.py @@ -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 @@ -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 @@ -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, @@ -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 diff --git a/examples/enable/tools/apptools/undoable_move_tool.py b/examples/enable/tools/apptools/undoable_move_tool.py index 935aba4bc..acae79653 100644 --- a/examples/enable/tools/apptools/undoable_move_tool.py +++ b/examples/enable/tools/apptools/undoable_move_tool.py @@ -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):