# (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!
from unittest.mock import Mock
from traits.api import Undefined
from enable.abstract_window import AbstractWindow
from enable.events import DragEvent, KeyEvent, MouseEvent
from kiva.testing import KivaTestAssistant
class _MockWindow(AbstractWindow):
# FIXME: for some reason I cannot replace these functions with a Mock
def _get_control_size(self):
return 0, 0
def _redraw(self, coordinates=None):
pass
_drag_result = None
def set_drag_result(self, result):
self._drag_result = result
[docs]class EnableTestAssistant(KivaTestAssistant):
""" Mixin helper for enable/chaco components.
"""
[docs] def press_move_release(self, interactor, points, window=None,
alt_down=False, control_down=False,
shift_down=False):
""" Simulate the action of left click pressing, dragging and releasing
the mouse.
Parameters
----------
interactor : enable interactor object
This is object where the mouse events will be dispatched to.
points : A list of x,y tuple
The x,y positions of the three event sections. The first tuple
will be sent with a left-down event and the last will be sent
with a left-up. All the other events in the list will be sent
using a mouse-move event.
window : AbstractWindow, optional
The enable AbstractWindow to associate with the event. Default
is to create a mock class instance. If the window has a mouse
owner then that interactor is used. If a non-Mocked window is
used, then either the test should not require the use of
get_pointer_position() or should use a mock for the method.
alt_down : boolean, optional
The button is pressed while `alt` is down. Default value is False.
control_down : boolean, optional
The button is pressed while `control` is down. Default value is
False.
shift_down : boolean, optional
The button is pressed while `shift` is down. Default value is
False.
"""
x, y = points[0]
window = self.create_mock_window() if window is None else window
self.mouse_down(
interactor,
x,
y,
"left",
window=window,
alt_down=alt_down,
control_down=control_down,
shift_down=shift_down,
)
for x, y in points[1:-1]:
self.mouse_move(
interactor,
x,
y,
window=window,
alt_down=alt_down,
control_down=control_down,
shift_down=shift_down,
left_down=True,
)
x, y = points[-1]
self.mouse_up(
interactor,
x,
y,
"left",
window=window,
alt_down=alt_down,
control_down=control_down,
shift_down=shift_down,
)
[docs] def create_mock_window(self):
""" Creates a Mock class that behaves as an enable Abstract Window.
Returns
-------
window : Mock
A mock class instance of an abstract window.
"""
window = _MockWindow()
window._capture_mouse = Mock()
window.set_pointer = Mock()
window._release_mouse = Mock()
window._redraw = Mock()
window.control = Mock()
window.control.set_pointer = Mock()
window.get_pointer_position = Mock()
return window
[docs] def create_key_press(self, key, window=None, alt_down=False,
control_down=False, shift_down=False):
""" Creates a KeyEvent for the given Key.
Parameters
----------
key : string
The key of the event
window : AbstractWindow, optional
The enable AbstractWindow to associate with the event. Default
is to create a mock class instance.
alt_down : boolean, optional
The key is pressed while `alt` is down. Default value is False.
control_down : boolean, optional
The key is pressed while `control` is down. Default value is
False.
shift_down : boolean, optional
The key is pressed while `shift` is down. Default value is
False.
Returns
-------
key_event : KeyEvent
The enable KEyEvent instance of the desired event ready to be
passed to an enable Interactor.
"""
key_event = KeyEvent(
character=key,
event_type="key_pressed",
alt_down=alt_down,
control_down=control_down,
shift_down=shift_down,
)
if window is None:
key_event.window = self.create_mock_window()
else:
key_event.window = window
return key_event
[docs] def create_mouse_event(self, **kwargs):
""" Creates a MouseEvent() with the specified attributes.
"""
# provide defaults for all key shift states
event_attributes = {
# key shift states
"alt_down": False,
"control_down": False,
"shift_down": False,
"mouse_wheel": 0,
"mouse_wheel_axis": "vertical",
}
event_attributes.update(**kwargs)
event = MouseEvent(**event_attributes)
return event
[docs] def create_drag_event(self, **kwargs):
""" Creates a DragEvent() with the specified attributes.
"""
event = DragEvent(**kwargs)
return event
[docs] def mouse_down(self, interactor, x, y, button="left", window=None,
alt_down=False, control_down=False, shift_down=False):
""" Send a mouse button down event to the interactor.
Parameters
----------
interactor : Interactor
The interactor (or subclass) where to dispatch the event.
x : float
The x coordinates of the mouse position
y : float
The y coordinates of the mouse position
button : {'left', 'right'}, optional
The mouse button for which to simulate a press (down) action.
window : AbstractWindow, optional
The enable AbstractWindow to associate with the event. Default
is to create a mock class instance. If the window has a mouse
owner then that interactor is used.
alt_down : boolean, optional
The button is pressed while `alt` is down. Default value is False.
control_down : boolean, optional
The button is pressed while `control` is down. Default value is
False.
shift_down : boolean, optional
The button is pressed while `shift` is down. Default value is
False.
Returns
-------
event : MouseEvent
The event instance after it has be processed by the `interactor`.
"""
window = self.create_mock_window() if window is None else window
event_attributes = {
"x": x,
"y": y,
"alt_down": alt_down,
"control_down": control_down,
"shift_down": shift_down,
"{0}_down".format(button): True,
"window": window,
}
event = self.create_mouse_event(**event_attributes)
self._mouse_event_dispatch(
interactor, event, "{0}_down".format(button)
)
return event
[docs] def mouse_dclick(self, interactor, x, y, button="left", window=None,
alt_down=False, control_down=False, shift_down=False):
""" Send a mouse double-click event to the interactor.
Parameters
----------
interactor : Interactor
The interactor (or subclass) where to dispatch the event.
x : float
The x coordinates of the mouse position
y : float
The y coordinates of the mouse position
button : {'left', 'right'}, optional
The mouse button for which to simulate a press (down) action.
window : AbstractWindow, optional
The enable AbstractWindow to associate with the event. Default
is to create a mock class instance. If the window has a mouse
owner then that interactor is used.
alt_down : boolean, optional
The button is pressed while `alt` is down. Default value is False.
control_down : boolean, optional
The button is pressed while `control` is down. Default value is
False.
shift_down : boolean, optional
The button is pressed while `shift` is down. Default value is
False.
Returns
-------
event : MouseEvent
The event instance after it has be processed by the `interactor`.
"""
window = self.create_mock_window() if window is None else window
event_attributes = {
"x": x,
"y": y,
"alt_down": alt_down,
"control_down": control_down,
"shift_down": shift_down,
"{0}_down".format(button): True,
"window": window,
}
event = self.create_mouse_event(**event_attributes)
self._mouse_event_dispatch(
interactor, event, "{0}_dclick".format(button)
)
return event
[docs] def mouse_move(self, interactor, x, y, window=None, alt_down=False,
control_down=False, shift_down=False, left_down=Undefined,
middle_down=Undefined, right_down=Undefined):
""" Send a mouse move event to the interactor.
Parameters
----------
interactor : Interactor
The interactor (or subclass) where to dispatch the event.
x : float
The x coordinates of the mouse position
y : float
The y coordinates of the mouse position
window : AbstractWindow, optional
The enable AbstractWindow to associate with the event. Default
is to create a mock class instance. If the window has a mouse
owner then that interactor is used. If a non-Mocked window is
used, then either the test should not require the use of
get_pointer_position() or should use a mock for the method.
alt_down : boolean, optional
The mouse is moved while `alt` is down. Default value is False.
control_down : boolean, optional
The mouse is moved while `control` is down. Default value is
False.
shift_down : boolean, optional
The mouse is moved while `shift` is down. Default value is
False.
left_down : boolean, optional
The mouse is moved while `left` is down. Default value is
Undefined.
middle_down : boolean, optional
The mouse is moved while `middle` is down. Default value is
Undefined.
right_down : boolean, optional
The mouse is moved while `right` is down. Default value is
Undefined.
Returns
-------
event : MouseEvent
The event instance after it has be processed by the `interactor`.
"""
window = self.create_mock_window() if window is None else window
event = self.create_mouse_event(
x=x,
y=y,
window=window,
alt_down=alt_down,
control_down=control_down,
shift_down=shift_down,
left_down=left_down,
middle_down=middle_down,
right_down=right_down,
)
if hasattr(window.get_pointer_position, "return_value"):
# Note: Non-mock windows shouldn't try to get pointer position
window.get_pointer_position.return_value = (x, y)
self._mouse_event_dispatch(interactor, event, "mouse_move")
return event
[docs] def mouse_up(self, interactor, x, y, button="left", window=None,
alt_down=False, control_down=False, shift_down=False):
""" Send a mouse button up event to the interactor.
Parameters
----------
interactor : Interactor
The interactor (or subclass) where to dispatch the event.
x : float
The x coordinates of the mouse position
y : float
The y coordinates of the mouse position
button : {'left', 'right'}, optional
The mouse button for which to simulate a release (up) action.
window : AbstractWindow, optional
The enable AbstractWindow to associate with the event. Default
is to create a mock class instance. If the window has a mouse
owner then that interactor is used.
alt_down : boolean, optional
The button is pressed while `alt` is down. Default value is False.
control_down : boolean, optional
The button is pressed while `control` is down. Default value is
False.
shift_down : boolean, optional
The button is pressed while `shift` is down. Default value is
False.
Returns
-------
event : MouseEvent
The event instance after it has be processed by the `interactor`.
"""
window = self.create_mock_window() if window is None else window
event = self.create_mouse_event(
x=x,
y=y,
window=window,
alt_down=alt_down,
control_down=control_down,
shift_down=shift_down,
)
self._mouse_event_dispatch(interactor, event, "{0}_up".format(button))
return event
[docs] def mouse_leave(self, interactor, x, y, window=None,
alt_down=False, control_down=False, shift_down=False):
""" Send a mouse leave event to the interactor.
Parameters
----------
interactor : Interactor
The interactor (or subclass) where to dispatch the event.
x : float
The x coordinates of the mouse position
y : float
The y coordinates of the mouse position
window : AbstractWindow, optional
The enable AbstractWindow to associate with the event. Default
is to create a mock class instance. If the window has a mouse
owner then that interactor is used.
alt_down : boolean, optional
The button is pressed while `alt` is down. Default value is False.
control_down : boolean, optional
The button is pressed while `control` is down. Default value is
False.
shift_down : boolean, optional
The button is pressed while `shift` is down. Default value is
False.
Returns
-------
event : MouseEvent
The event instance after it has be processed by the `interactor`.
"""
window = self.create_mock_window() if window is None else window
event = self.create_mouse_event(
x=x,
y=y,
window=window,
alt_down=alt_down,
control_down=control_down,
shift_down=shift_down,
)
self._mouse_event_dispatch(interactor, event, "mouse_leave")
return event
[docs] def mouse_enter(self, interactor, x, y, window=None,
alt_down=False, control_down=False, shift_down=False):
""" Send a mouse enter event to the interactor.
Parameters
----------
interactor : Interactor
The interactor (or subclass) where to dispatch the event.
x : float
The x coordinates of the mouse position
y : float
The y coordinates of the mouse position
window : AbstractWindow, optional
The enable AbstractWindow to associate with the event. Default
is to create a mock class instance. If the window has a mouse
owner then that interactor is used.
alt_down : boolean, optional
The button is pressed while `alt` is down. Default value is False.
control_down : boolean, optional
The button is pressed while `control` is down. Default value is
False.
shift_down : boolean, optional
The button is pressed while `shift` is down. Default value is
False.
Returns
-------
event : MouseEvent
The event instance after it has be processed by the `interactor`.
"""
window = self.create_mock_window() if window is None else window
event = self.create_mouse_event(
x=x,
y=y,
window=window,
alt_down=alt_down,
control_down=control_down,
shift_down=shift_down,
)
self._mouse_event_dispatch(interactor, event, "mouse_enter")
return event
[docs] def send_key(self, interactor, key, window=None):
""" Sent a key press event to the interactor.
Parameters
----------
interactor : Interactor
The interactor (or subclass) where to dispatch the event.
key : string
The key press to simulate.
window : AbstractWindow, optional
The enable AbstractWindow to associate with the event. Default
is to create a mock class instance. If the window has a focus
owner then that interactor is used.
Returns
-------
event : KeyEvent
The event instance after it has be processed by the `interactor`.
"""
window = self.create_mock_window() if window is None else window
event = self.create_key_press(key, window=window)
self._key_event_dispatch(interactor, event)
return event
[docs] def send_drag_over(self, interactor, x, y, obj, window=None):
""" Sent a drag_over event to the interactor.
Parameters
----------
interactor : Interactor
The interactor (or subclass) where to dispatch the event.
x : float
The x coordinates of the mouse position
y : float
The y coordinates of the mouse position
obj : object
The object(s) being dragged or dropped
window : AbstractWindow, optional
The enable AbstractWindow to associate with the event. Default
is to create a mock class instance. If the window has a mouse
owner then that interactor is used.
Returns
-------
event : KeyEvent
The event instance after it has be processed by the `interactor`.
"""
window = self.create_mock_window() if window is None else window
event = self.create_drag_event(x=x, y=y, obj=obj, window=window)
self._drag_event_dispatch(interactor, event, "drag_over")
return event
[docs] def send_dropped_on(self, interactor, x, y, obj, window=None):
""" Sent a dropped_on event to the interactor.
Parameters
----------
interactor : Interactor
The interactor (or subclass) where to dispatch the event.
x : float
The x coordinates of the mouse position
y : float
The y coordinates of the mouse position
obj : object
The object(s) being dragged or dropped
window : AbstractWindow, optional
The enable AbstractWindow to associate with the event. Default
is to create a mock class instance. If the window has a mouse
owner then that interactor is used.
Returns
-------
event : KeyEvent
The event instance after it has be processed by the `interactor`.
"""
window = self.create_mock_window() if window is None else window
event = self.create_drag_event(x=x, y=y, obj=obj, window=window)
self._drag_event_dispatch(interactor, event, "dropped_on")
return event
[docs] def send_drag_leave(self, interactor, x, y, window=None):
""" Sent a drag_leave event to the interactor.
Parameters
----------
interactor : Interactor
The interactor (or subclass) where to dispatch the event.
x : float
The x coordinates of the mouse position
y : float
The y coordinates of the mouse position
window : AbstractWindow, optional
The enable AbstractWindow to associate with the event. Default
is to create a mock class instance. If the window has a mouse
owner then that interactor is used.
Returns
-------
event : KeyEvent
The event instance after it has be processed by the `interactor`.
"""
window = self.create_mock_window() if window is None else window
event = self.create_drag_event(x=x, y=y, window=window)
self._drag_event_dispatch(interactor, event, "drag_leave")
return event
def _mouse_event_dispatch(self, interactor, event, suffix):
mouse_owner = event.window.mouse_owner
if mouse_owner is None:
interactor.dispatch(event, suffix)
else:
mouse_owner.dispatch(event, suffix)
def _key_event_dispatch(self, interactor, event):
focus_owner = event.window.focus_owner
if focus_owner is None:
interactor.dispatch(event, "key_pressed")
else:
focus_owner.dispatch(event, "key_pressed")
def _drag_event_dispatch(self, interactor, event, suffix):
interactor.dispatch(event, suffix)