563 lines
18 KiB
Python
563 lines
18 KiB
Python
from kivy.tests.common import GraphicUnitTest
|
|
from kivy.input.providers.mouse import (
|
|
MouseMotionEventProvider as Mouse
|
|
)
|
|
|
|
|
|
class MultitouchSimulatorTestCase(GraphicUnitTest):
|
|
framecount = 0
|
|
|
|
# helper methods
|
|
def correct_y(self, win, y):
|
|
# flip, because the mouse provider uses system's
|
|
# raw one and it's changed to bottom-left origin
|
|
# with Window's system_size[1] for 'mouse_pos'
|
|
return win.height - y
|
|
|
|
def mouse_init(self, on_demand=False, disabled=False, scatter=False):
|
|
# prepare MouseMotionEventProvider
|
|
# and widget it interacts with
|
|
from kivy.base import EventLoop
|
|
from kivy.uix.button import Button
|
|
from kivy.uix.scatter import Scatter
|
|
|
|
eventloop = EventLoop
|
|
win = eventloop.window
|
|
eventloop.idle()
|
|
wid = Scatter() if scatter else Button()
|
|
|
|
if on_demand:
|
|
mode = 'multitouch_on_demand'
|
|
elif disabled:
|
|
mode = 'disable_multitouch'
|
|
else:
|
|
mode = ''
|
|
mouse = Mouse('unittest', mode)
|
|
mouse.is_touch = True
|
|
|
|
# defaults from ME, it's missing because we use
|
|
# the provider directly instead of ME
|
|
mouse.scale_for_screen = lambda *_, **__: None
|
|
mouse.grab_exclusive_class = None
|
|
mouse.grab_list = []
|
|
|
|
if on_demand:
|
|
self.assertTrue(mouse.multitouch_on_demand)
|
|
return (eventloop, win, mouse, wid)
|
|
|
|
def multitouch_dot_touch(self, button, **kwargs):
|
|
# touch -> dot appears -> touch again -> dot disappears
|
|
eventloop, win, mouse, wid = self.mouse_init(**kwargs)
|
|
|
|
# register mouse provider
|
|
mouse.start()
|
|
eventloop.add_input_provider(mouse)
|
|
|
|
# no mouse touch anywhere
|
|
self.assertEqual(mouse.counter, 0)
|
|
self.assertEqual(mouse.touches, {})
|
|
|
|
# right button down, red dot should appear
|
|
win.dispatch(
|
|
'on_mouse_down',
|
|
10, self.correct_y(win, 10),
|
|
'right', {}
|
|
)
|
|
self.assertEqual(mouse.counter, 1)
|
|
|
|
if 'on_demand' in kwargs and 'scatter' not in kwargs:
|
|
# doesn't do anything on a pure Button
|
|
self.render(wid)
|
|
|
|
# cleanup!
|
|
# remove mouse provider
|
|
mouse.stop()
|
|
eventloop.remove_input_provider(mouse)
|
|
return
|
|
|
|
elif 'on_demand' in kwargs and 'scatter' in kwargs:
|
|
self.assertIn(
|
|
'multitouch_sim',
|
|
mouse.touches['mouse1'].profile
|
|
)
|
|
self.assertTrue(mouse.multitouch_on_demand)
|
|
|
|
# multitouch_sim is changed in on_touch_down
|
|
# method of the widget that's able to handle
|
|
# multiple touches, therefore for Scatter we
|
|
# need to dispatch the method and because we
|
|
# triggered only on_mouse_down directly i.e.
|
|
# without ME dispatch, on_touch_down was not
|
|
# called == multitouch_sim is False
|
|
self.advance_frames(1) # initialize stuff
|
|
wid.on_touch_down(mouse.touches['mouse1'])
|
|
wid.on_touch_up(mouse.touches['mouse1'])
|
|
self.assertTrue(mouse.touches['mouse1'].multitouch_sim)
|
|
|
|
elif 'disabled' in kwargs:
|
|
self.assertIsNone(
|
|
mouse.touches['mouse1'].ud.get('_drawelement')
|
|
) # the red dot isn't present
|
|
|
|
else:
|
|
self.assertIsNotNone(
|
|
mouse.touches['mouse1'].ud.get('_drawelement')
|
|
) # the red dot is present
|
|
|
|
# XXX right button up
|
|
# first release the touch then check, so that we
|
|
# have the red dot drawn in on_demand and in the
|
|
# default (multitouch everywhere) because in the
|
|
# multitouch_on_demand is the circle drawn after
|
|
# the touch is released (in on_mouse_release)
|
|
win.dispatch(
|
|
'on_mouse_up',
|
|
10, self.correct_y(win, 10),
|
|
'right', {}
|
|
)
|
|
|
|
self.assertEqual(mouse.counter, 1)
|
|
|
|
# because the red dot is removed by the left button
|
|
if 'disabled' not in kwargs:
|
|
self.assertIn('mouse1', mouse.touches)
|
|
self.assertIsNotNone(
|
|
mouse.touches['mouse1'].ud.get('_drawelement')
|
|
) # the red dot is present
|
|
|
|
# button is down on the previous dot's position
|
|
win.dispatch(
|
|
'on_mouse_down',
|
|
10, self.correct_y(win, 10),
|
|
button, {}
|
|
)
|
|
# if the multitouch is disabled, the touch event
|
|
# increments the counter
|
|
self.assertEqual(
|
|
mouse.counter,
|
|
1 + int('disabled' in kwargs)
|
|
)
|
|
if 'disabled' in kwargs:
|
|
# the right click is ignored, test ends here
|
|
self.assertNotIn(
|
|
'mouse1', mouse.touches
|
|
)
|
|
# cleanup!
|
|
# remove mouse provider
|
|
mouse.stop()
|
|
eventloop.remove_input_provider(mouse)
|
|
return
|
|
else:
|
|
self.assertIsNotNone(
|
|
mouse.touches['mouse1'].ud.get('_drawelement')
|
|
) # the red dot is present
|
|
|
|
# ellipse proxy (<3 #1318 Instruction.proxy_ref)
|
|
dot_proxy = mouse.touches[
|
|
'mouse1'
|
|
].ud.get('_drawelement')[1].proxy_ref
|
|
|
|
# the dot is removed after the touch is released
|
|
# when right - touch is preserved -> dot remains
|
|
# when left - touch is destroyed -> dot removed
|
|
win.dispatch(
|
|
'on_mouse_up',
|
|
10, self.correct_y(win, 10),
|
|
button, {}
|
|
) # no matter where
|
|
|
|
# the touch, which holds the only ref to the dot
|
|
# instance (Ellipse) is collected, therefore the
|
|
# proxy can confirm the dot is removed
|
|
# (indirect ref at least + it would be nasty for
|
|
# checking if the ellipse remained on visible on
|
|
# the Canvas after being GC-ed if not impossible
|
|
# without the Instruction object trick ._. )
|
|
if button == 'left':
|
|
with self.assertRaises(ReferenceError):
|
|
print(dot_proxy)
|
|
|
|
self.assertEqual(mouse.counter, 1)
|
|
self.assertNotIn('mouse1', mouse.touches)
|
|
self.assertEqual(mouse.touches, {})
|
|
|
|
elif button == 'right':
|
|
self.assertEqual(mouse.counter, 1)
|
|
self.assertIn('mouse1', mouse.touches)
|
|
self.assertIsNotNone(
|
|
mouse.touches['mouse1'].ud.get('_drawelement')
|
|
) # the red dot is present
|
|
|
|
self.render(wid)
|
|
|
|
# cleanup!
|
|
# remove mouse provider
|
|
mouse.stop()
|
|
eventloop.remove_input_provider(mouse)
|
|
|
|
def multitouch_dot_move(self, button, **kwargs):
|
|
# touch -> dot appears -> move touch -> dot moves
|
|
# -> release touch -> touch & dot disappear
|
|
eventloop, win, mouse, wid = self.mouse_init(**kwargs)
|
|
|
|
# register mouse provider
|
|
mouse.start()
|
|
eventloop.add_input_provider(mouse)
|
|
|
|
# no mouse touch anywhere
|
|
self.assertEqual(mouse.counter, 0)
|
|
self.assertEqual(mouse.touches, {})
|
|
|
|
# right button down, red dot should appear
|
|
# if the 'multitouch_on_demand' is disabled
|
|
win.dispatch(
|
|
'on_mouse_down',
|
|
10, self.correct_y(win, 10),
|
|
'right', {}
|
|
)
|
|
self.assertEqual(mouse.counter, 1)
|
|
|
|
if 'on_demand' in kwargs and 'scatter' not in kwargs:
|
|
# doesn't do anything on a pure Button
|
|
self.render(wid)
|
|
|
|
# cleanup!
|
|
# remove mouse provider
|
|
mouse.stop()
|
|
eventloop.remove_input_provider(mouse)
|
|
return
|
|
|
|
# XXX right button up
|
|
# first release the touch then check, so that we
|
|
# have the red dot drawn in on_demand and in the
|
|
# default (multitouch everywhere) because in the
|
|
# multitouch_on_demand is the circle drawn after
|
|
# the touch is released (in on_mouse_release)
|
|
elif 'on_demand' in kwargs and 'scatter' in kwargs:
|
|
# on_demand works after the touch is up
|
|
self.assertIn(
|
|
'multitouch_sim',
|
|
mouse.touches['mouse1'].profile
|
|
)
|
|
self.assertTrue(mouse.multitouch_on_demand)
|
|
|
|
# multitouch_sim is changed in on_touch_down
|
|
# method of the widget that's able to handle
|
|
# multiple touches, therefore for Scatter we
|
|
# need to dispatch the method and because we
|
|
# triggered only on_mouse_down directly i.e.
|
|
# without ME dispatch, on_touch_down was not
|
|
# called == multitouch_sim is False
|
|
self.advance_frames(1) # initialize stuff
|
|
wid.on_touch_down(mouse.touches['mouse1'])
|
|
wid.on_touch_up(mouse.touches['mouse1'])
|
|
self.assertTrue(mouse.touches['mouse1'].multitouch_sim)
|
|
|
|
win.dispatch(
|
|
'on_mouse_up',
|
|
10, self.correct_y(win, 10),
|
|
'right', {}
|
|
)
|
|
color = mouse.touches[
|
|
'mouse1'
|
|
].ud.get('_drawelement')[0].proxy_ref
|
|
ellipse = mouse.touches[
|
|
'mouse1'
|
|
].ud.get('_drawelement')[1].proxy_ref
|
|
win.dispatch(
|
|
'on_mouse_down',
|
|
10, self.correct_y(win, 10),
|
|
'right', {}
|
|
)
|
|
|
|
elif 'disabled' in kwargs:
|
|
self.assertIsNone(
|
|
mouse.touches['mouse1'].ud.get('_drawelement')
|
|
) # the red dot isn't present
|
|
|
|
else:
|
|
self.assertIsNotNone(
|
|
mouse.touches['mouse1'].ud.get('_drawelement')
|
|
) # the red dot is present
|
|
|
|
# do NOT make any hard refs to '_drawelement'
|
|
if 'disabled' in kwargs:
|
|
# the right click doesn't draw the red dot
|
|
# the instructions aren't present, test ends
|
|
self.assertIsNone(
|
|
mouse.touches['mouse1'].ud.get('_drawelement')
|
|
) # the red dot isn't present
|
|
# cleanup!
|
|
# remove mouse provider
|
|
mouse.stop()
|
|
eventloop.remove_input_provider(mouse)
|
|
return
|
|
|
|
else:
|
|
color = mouse.touches[
|
|
'mouse1'
|
|
].ud.get('_drawelement')[0].proxy_ref
|
|
ellipse = mouse.touches[
|
|
'mouse1'
|
|
].ud.get('_drawelement')[1].proxy_ref
|
|
|
|
# the red dot moves when the touch is moving
|
|
win.dispatch(
|
|
'on_mouse_move',
|
|
11, self.correct_y(win, 11),
|
|
{}
|
|
)
|
|
self.assertEqual(
|
|
ellipse.pos,
|
|
(1, self.correct_y(win, win.height - 1))
|
|
) # bounding box from Rectangle, R=10 -> 20 width
|
|
|
|
# right button up
|
|
win.dispatch(
|
|
'on_mouse_up',
|
|
10, self.correct_y(win, 10),
|
|
'right', {}
|
|
)
|
|
self.assertEqual(mouse.counter, 1)
|
|
|
|
# because the red dot is removed by the left button
|
|
self.assertIn('mouse1', mouse.touches)
|
|
self.assertIsNotNone(
|
|
mouse.touches['mouse1'].ud.get('_drawelement')
|
|
) # the red dot is present
|
|
|
|
# the dot is at (11, 11), but the touch is in
|
|
# its bounding box, therefore it can move it
|
|
win.dispatch(
|
|
'on_mouse_down',
|
|
10, self.correct_y(win, 10),
|
|
button, {}
|
|
)
|
|
|
|
# manipulating already existing touch,
|
|
# no new one was created
|
|
self.assertEqual(mouse.counter, 1)
|
|
self.assertIsNotNone(
|
|
mouse.touches['mouse1'].ud.get('_drawelement')
|
|
) # the red dot is present
|
|
|
|
# the red dot moves when the touch is moving
|
|
win.dispatch(
|
|
'on_mouse_move',
|
|
50, self.correct_y(win, 50),
|
|
{}
|
|
)
|
|
self.assertEqual(
|
|
ellipse.pos,
|
|
(40, self.correct_y(win, win.height - 40))
|
|
) # bounding box from Rectangle, R=10 -> 20 width
|
|
|
|
# the dot is removed after the touch is released
|
|
# when right - touch is preserved -> dot remains
|
|
# when left - touch is destroyed -> dot removed
|
|
win.dispatch(
|
|
'on_mouse_up',
|
|
10, self.correct_y(win, 10),
|
|
button, {}
|
|
) # no matter where
|
|
self.assertEqual(mouse.counter, 1)
|
|
|
|
if button == 'left':
|
|
self.assertNotIn('mouse1', mouse.touches)
|
|
elif button == 'right':
|
|
self.assertIn('mouse1', mouse.touches)
|
|
self.assertIsNotNone(
|
|
mouse.touches['mouse1'].ud.get('_drawelement')
|
|
) # the red dot is present
|
|
|
|
self.render(wid)
|
|
|
|
# cleanup!
|
|
# remove mouse provider
|
|
mouse.stop()
|
|
eventloop.remove_input_provider(mouse)
|
|
|
|
# tests
|
|
def test_multitouch_dontappear(self):
|
|
eventloop, win, mouse, wid = self.mouse_init()
|
|
|
|
# register mouse provider
|
|
mouse.start()
|
|
eventloop.add_input_provider(mouse)
|
|
|
|
# no mouse touch anywhere
|
|
self.assertEqual(mouse.counter, 0)
|
|
self.assertEqual(mouse.touches, {})
|
|
|
|
# left button down
|
|
win.dispatch(
|
|
'on_mouse_down',
|
|
10, self.correct_y(win, 10),
|
|
'left', {}
|
|
)
|
|
win.dispatch(
|
|
'on_mouse_move',
|
|
11, self.correct_y(win, 11),
|
|
{}
|
|
)
|
|
self.assertEqual(mouse.counter, 1)
|
|
self.assertIsNone(
|
|
mouse.touches['mouse1'].ud.get('_drawelement')
|
|
) # the red dot isn't present
|
|
|
|
# left button up
|
|
win.dispatch(
|
|
'on_mouse_up',
|
|
10, self.correct_y(win, 10),
|
|
'left', {}
|
|
)
|
|
# after the releasing the touch disappears,
|
|
# but the counter remains
|
|
self.assertEqual(mouse.counter, 1)
|
|
self.assertNotIn('mouse1', mouse.touches)
|
|
|
|
self.advance_frames(1)
|
|
self.render(wid)
|
|
|
|
# cleanup!
|
|
# remove mouse provider
|
|
mouse.stop()
|
|
eventloop.remove_input_provider(mouse)
|
|
|
|
def test_multitouch_appear(self):
|
|
eventloop, win, mouse, wid = self.mouse_init()
|
|
|
|
# register mouse provider
|
|
mouse.start()
|
|
eventloop.add_input_provider(mouse)
|
|
|
|
# no mouse touch anywhere
|
|
self.assertEqual(mouse.counter, 0)
|
|
self.assertEqual(mouse.touches, {})
|
|
|
|
# right button down, red dot should appear
|
|
win.dispatch(
|
|
'on_mouse_down',
|
|
10, self.correct_y(win, 10),
|
|
'right', {}
|
|
)
|
|
self.assertEqual(mouse.counter, 1)
|
|
self.assertIsNotNone(
|
|
mouse.touches['mouse1'].ud.get('_drawelement')
|
|
) # the red dot is present
|
|
|
|
# do NOT make any hard refs to '_drawelement'
|
|
color = mouse.touches[
|
|
'mouse1'
|
|
].ud.get('_drawelement')[0].proxy_ref
|
|
ellipse = mouse.touches[
|
|
'mouse1'
|
|
].ud.get('_drawelement')[1].proxy_ref
|
|
|
|
# check ellipse's position
|
|
self.assertEqual(
|
|
ellipse.pos[0], 0
|
|
) # bounding box from Rectangle, R=10 -> 20 width
|
|
# almost equal because the correct_y uses the same
|
|
# float - float, which returns decimal garbage
|
|
self.assertAlmostEqual(
|
|
ellipse.pos[1],
|
|
self.correct_y(win, win.height),
|
|
delta=0.0001
|
|
)
|
|
|
|
win.dispatch(
|
|
'on_mouse_move',
|
|
11, self.correct_y(win, 11),
|
|
{}
|
|
)
|
|
# the red dot moves when the touch is moving
|
|
self.assertEqual(
|
|
ellipse.pos,
|
|
(1, self.correct_y(win, win.height - 1))
|
|
) # bounding box from Rectangle, R=10 -> 20 width
|
|
win.dispatch(
|
|
'on_mouse_up',
|
|
10, self.correct_y(win, 10),
|
|
'right', {}
|
|
)
|
|
|
|
self.assertEqual(
|
|
ellipse.pos,
|
|
(1, self.correct_y(win, win.height - 1))
|
|
) # bounding box from Rectangle, R=10 -> 20 width
|
|
self.assertEqual(mouse.counter, 1)
|
|
# because the red dot is removed by the left button
|
|
self.assertIn('mouse1', mouse.touches)
|
|
self.assertIsNotNone(
|
|
mouse.touches['mouse1'].ud.get('_drawelement')
|
|
) # the red dot is present
|
|
|
|
self.render(wid)
|
|
|
|
# cleanup!
|
|
# remove mouse provider
|
|
mouse.stop()
|
|
eventloop.remove_input_provider(mouse)
|
|
|
|
def test_multitouch_dot_lefttouch(self):
|
|
self.multitouch_dot_touch('left')
|
|
|
|
def test_multitouch_dot_leftmove(self):
|
|
self.multitouch_dot_move('left')
|
|
|
|
def test_multitouch_dot_righttouch(self):
|
|
self.multitouch_dot_touch('right')
|
|
|
|
def test_multitouch_dot_rightmove(self):
|
|
self.multitouch_dot_move('right')
|
|
|
|
def test_multitouch_on_demand_noscatter_lefttouch(self):
|
|
self.multitouch_dot_touch('left', on_demand=True)
|
|
|
|
def test_multitouch_on_demand_noscatter_leftmove(self):
|
|
self.multitouch_dot_move('left', on_demand=True)
|
|
|
|
def test_multitouch_on_demand_noscatter_righttouch(self):
|
|
self.multitouch_dot_touch('right', on_demand=True)
|
|
|
|
def test_multitouch_on_demand_noscatter_rightmove(self):
|
|
self.multitouch_dot_move('right', on_demand=True)
|
|
|
|
def test_multitouch_on_demand_scatter_lefttouch(self):
|
|
self.multitouch_dot_touch(
|
|
'left', on_demand=True, scatter=True
|
|
)
|
|
|
|
def test_multitouch_on_demand_scatter_leftmove(self):
|
|
self.multitouch_dot_move(
|
|
'left', on_demand=True, scatter=True
|
|
)
|
|
|
|
def test_multitouch_on_demand_scatter_righttouch(self):
|
|
self.multitouch_dot_touch(
|
|
'right', on_demand=True, scatter=True
|
|
)
|
|
|
|
def test_multitouch_on_demand_scatter_rightmove(self):
|
|
self.multitouch_dot_move(
|
|
'right', on_demand=True, scatter=True
|
|
)
|
|
|
|
def test_multitouch_disabled_lefttouch(self):
|
|
self.multitouch_dot_touch('left', disabled=True)
|
|
|
|
def test_multitouch_disabled_leftmove(self):
|
|
self.multitouch_dot_move('left', disabled=True)
|
|
|
|
def test_multitouch_disabled_righttouch(self):
|
|
self.multitouch_dot_touch('right', disabled=True)
|
|
|
|
def test_multitouch_disabled_rightmove(self):
|
|
self.multitouch_dot_move('right', disabled=True)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
import unittest
|
|
unittest.main()
|