class FakeEvent:
- def __init__(self, widget, num=1):
+ def __init__(self, widget, num=1, x_root=0, y_root=0):
self.num = num
self.widget = widget
- self.x = self.y = self.x_root = self.y_root = 0
+ self.x = self.y = 0
+ self.x_root = x_root
+ self.y_root = y_root
class DndTest(AbstractTkTest, unittest.TestCase):
handler.cancel()
def test_drag_cursor(self):
+ # The drag cursor is not shown on the initial press, only once the
+ # pointer moves past the threshold, so a plain click does not flash
+ # it (gh-43699). The original cursor is restored afterwards.
self.canvas['cursor'] = 'watch'
handler = dnd.dnd_start(self.source, FakeEvent(self.canvas))
- # The drag cursor is shown while dragging, the original restored after.
self.assertEqual(handler.save_cursor, 'watch')
+ self.assertEqual(str(self.canvas['cursor']), 'watch')
+ handler.on_motion(FakeEvent(self.canvas, x_root=2)) # below threshold
+ self.assertEqual(str(self.canvas['cursor']), 'watch')
+ handler.on_motion(FakeEvent(self.canvas, x_root=20)) # past threshold
self.assertEqual(str(self.canvas['cursor']), 'hand2')
handler.cancel()
self.assertEqual(str(self.canvas['cursor']), 'watch')
root = None
+ # The drag cursor is shown only once the pointer has moved this many
+ # pixels from the initial press, so that a plain click does not flash it.
+ threshold = 3
+
def __init__(self, source, event):
if event.num > 5:
return
self.target = None
self.initial_button = button = event.num
self.initial_widget = widget = event.widget
+ self.dragging = False
+ self.x_origin, self.y_origin = event.x_root, event.y_root
self.release_pattern = "<B%d-ButtonRelease-%d>" % (button, button)
self.save_cursor = widget['cursor'] or ""
widget.bind(self.release_pattern, self.on_release)
widget.bind("<Motion>", self.on_motion)
- widget['cursor'] = "hand2"
def __del__(self):
root = self.root
if new_target is not None:
new_target.dnd_enter(source, event)
self.target = new_target
+ self.update_cursor(x, y)
+
+ def update_cursor(self, x, y):
+ # Show the drag cursor only once the pointer has actually started
+ # moving past the threshold, so that a plain click does not flash it.
+ if not self.dragging:
+ if (abs(x - self.x_origin) <= self.threshold and
+ abs(y - self.y_origin) <= self.threshold):
+ return
+ self.dragging = True
+ self.initial_widget['cursor'] = "hand2"
def on_release(self, event):
self.finish(event, 1)
--- /dev/null
+The drag cursor in :mod:`tkinter.dnd` is now changed only once the pointer
+starts moving rather than on the initial button press, so that a plain
+click no longer flashes it.