Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 47 additions & 1 deletion enable/tests/test_drag_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#
# Thanks for using Enthought open source!
import unittest
import warnings

from traits.api import Int

Expand All @@ -19,10 +20,16 @@ class DummyTool(DragTool):

canceled = Int

ended = Int

def drag_cancel(self, event):
self.canceled += 1
return True

def drag_end(self, event):
self.ended += 1
return True


class DragToolTestCase(EnableTestAssistant, unittest.TestCase):
def setUp(self):
Expand Down Expand Up @@ -68,7 +75,8 @@ def test_mouse_leave_drag_state(self):
tool = self.tool
tool.end_drag_on_leave = True
tool._drag_state = "dragging" # force dragging state
event = self.mouse_leave(interactor=tool, x=0, y=0)
with self.assertWarns(DeprecationWarning):
event = self.mouse_leave(interactor=tool, x=0, y=0)
self.assertEqual(tool.canceled, 1)
self.assertEqual(tool._drag_state, "nondrag")
self.assertTrue(event.handled)
Expand All @@ -82,3 +90,41 @@ def test_mouse_leave_drag_state(self):
self.assertEqual(tool.canceled, 1)
self.assertEqual(tool._drag_state, "dragging")
self.assertFalse(event.handled)

def test_on_drag_leave(self):
# When on_drag_leave is 'cancel' then the drag_cancel is called
# and the _drag_state will be 'nondrag'
tool = self.tool
tool.on_drag_leave = 'cancel'
tool._drag_state = "dragging" # force dragging state
event = self.mouse_leave(interactor=tool, x=0, y=0)
self.assertEqual(tool.canceled, 1)
self.assertEqual(tool._drag_state, "nondrag")
self.assertTrue(event.handled)

# When on_drag_leave is 'end' then the drag_end is called
# and the _drag_state will be 'nondrag'
tool.on_drag_leave = 'end'
tool._drag_state = "dragging" # force dragging state
event = self.mouse_leave(interactor=tool, x=0, y=0)
self.assertEqual(tool.ended, 1)
self.assertEqual(tool._drag_state, "nondrag")
self.assertTrue(event.handled)

def test_on_drag_leave_no_op(self):
""" If end_drag_on_leave is set, on_drag_leave trait is ignored. """

tool = self.tool
tool.end_drag_on_leave = True
tool.on_drag_leave = 'end'
tool._drag_state = "dragging" # force dragging state
with self.assertWarns(DeprecationWarning):
event = self.mouse_leave(interactor=tool, x=0, y=0)

# end_drag_on_leave should be handled like normal
self.assertEqual(tool.canceled, 1)
self.assertEqual(tool._drag_state, "nondrag")
self.assertTrue(event.handled)

# even though on_drag_leave = 'end' we do nothing
self.assertEqual(tool.ended, 0)
48 changes: 42 additions & 6 deletions enable/tools/drag_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
# Thanks for using Enthought open source!
""" Defines the base DragTool class.
"""
import warnings

# Enthought library imports
from enable.base_tool import BaseTool, KeySpec
from traits.api import Bool, Enum, List, Property, Str, Tuple, cached_property
Expand All @@ -24,11 +26,19 @@ class DragTool(BaseTool):
# The mouse button used for this drag operation.
drag_button = Enum("left", "right")

# End the drag operation if the mouse leaves the associated component?
# Deprecated; on_drag_leave is the new, more flexible / intuitive means for
# providing this functionality
# Cancel the drag operation if the mouse leaves the associated component?
# NOTE: This behavior depends on "mouse_leave" events, which in general
# are not fired when `capture_mouse` is True (default).
end_drag_on_leave = Bool(False)

# Do nothing, cancel or end the drag operation if the mouse leaves the
# associated component?
# NOTE: This behavior depends on "mouse_leave" events, which in general
# are not fired when `capture_mouse` is True (default).
Comment on lines +36 to +39
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe I should make it more clear here that on_drag_leave = 'cancel' is equivalent to end_drag_on_leave = True not on_drag_leave = 'end'? That confusion is sort of the root problem at hand

on_drag_leave = Enum(None, 'cancel', 'end')
Comment thread
jwiggins marked this conversation as resolved.

# These keys, if pressed during drag, cause the drag operation to reset.
cancel_keys = List(Str, ["Esc"])

Expand All @@ -43,7 +53,7 @@ class DragTool(BaseTool):
# Whether or not to capture the mouse during the drag operation. In effect,
# this routes mouse events back to this tool for dispatching, rather than
# allowing the event to be handled by the window. This may have effects
# surrounding "mouse_leave" events: see note on `end_drag_on_leave` flag.
# surrounding "mouse_leave" events: see note on `on_drag_leave` flag.
capture_mouse = Bool(True)

# ------------------------------------------------------------------------
Expand Down Expand Up @@ -98,13 +108,16 @@ def drag_cancel(self, event):
""" Called when the drag is cancelled.

A drag is usually cancelled by receiving a mouse_leave event when
end_drag_on_leave is True, or by the user pressing any of the
**cancel_keys**.
end_drag_on_leave is True, or on_drag_leave is 'cancel', or by the user
pressing any of the **cancel_keys**.
"""
pass

def drag_end(self, event):
""" Called when a mouse event causes the drag operation to end.

A drag is ended when a user releases the mouse, or by receiving a
mouse_leave event when on_drag_leave is 'end'.
"""
pass

Expand Down Expand Up @@ -139,7 +152,15 @@ def _cancel_drag(self, event):
self._drag_state = "nondrag"
outcome = self.drag_cancel(event)
self._mouse_down_received = False
if event.window.mouse_owner == self:
if event.window.mouse_owner is self:
event.window.set_mouse_owner(None)
return outcome

def _end_drag(self, event):
self._drag_state = "nondrag"
outcome = self.drag_end(event)
self._mouse_down_received = False
if event.window.mouse_owner is self:
event.window.set_mouse_owner(None)
return outcome

Expand Down Expand Up @@ -197,8 +218,23 @@ def _drag_button_up(self, event):
return False

def _drag_mouse_leave(self, event):
if self.end_drag_on_leave and self._drag_state == "dragging":
if self.end_drag_on_leave:
# raise deprecation warning
msg = ("end_drag_on_leave is now deprecated as its name was "
"misleading. It triggers a drag_cancel not drag_end on "
"leave. Use new on_drag_end Enum trait instead.")
warnings.warn(
msg,
category=DeprecationWarning,
)
if self._drag_state == "dragging":
return self._cancel_drag(event)
return False

if self.on_drag_leave == "cancel" and self._drag_state == "dragging":
return self._cancel_drag(event)
elif self.on_drag_leave == "end" and self._drag_state == "dragging":
return self._end_drag(event)
return False

def _drag_mouse_enter(self, event):
Expand Down