@@ -60,6 +60,15 @@ def setUp(self): |
60 | 60 | self.source = Source(self.log) |
61 | 61 | self.target = Target(self.canvas, self.log) |
62 | 62 |
|
| 63 | + def tearDown(self): |
| 64 | + # Make sure no drag-and-drop is left active between tests: the |
| 65 | + # recursion guard is a name-mangled attribute on the root. |
| 66 | + try: |
| 67 | + del self.root._DndHandler__dnd |
| 68 | + except AttributeError: |
| 69 | + pass |
| 70 | + super().tearDown() |
| 71 | + |
63 | 72 | def test_drag_and_drop(self): |
64 | 73 | handler = dnd.dnd_start(self.source, FakeEvent(self.canvas)) |
65 | 74 | self.assertIsNotNone(handler) |
@@ -93,6 +102,70 @@ def test_no_recursive_start(self): |
93 | 102 | def test_high_button_number_ignored(self): |
94 | 103 | self.assertIsNone(dnd.dnd_start(self.source, FakeEvent(self.canvas, num=6))) |
95 | 104 |
|
| 105 | + def test_restart_after_finish(self): |
| 106 | + handler = dnd.dnd_start(self.source, FakeEvent(self.canvas)) |
| 107 | + handler.cancel() |
| 108 | + # Once a drag has finished a new one can start. |
| 109 | + handler = dnd.dnd_start(self.source, FakeEvent(self.canvas)) |
| 110 | + self.assertIsNotNone(handler) |
| 111 | + handler.cancel() |
| 112 | + |
| 113 | + def test_drag_cursor(self): |
| 114 | + self.canvas['cursor'] = 'watch' |
| 115 | + handler = dnd.dnd_start(self.source, FakeEvent(self.canvas)) |
| 116 | + # The drag cursor is shown while dragging, the original restored after. |
| 117 | + self.assertEqual(handler.save_cursor, 'watch') |
| 118 | + self.assertEqual(str(self.canvas['cursor']), 'hand2') |
| 119 | + handler.cancel() |
| 120 | + self.assertEqual(str(self.canvas['cursor']), 'watch') |
| 121 | + |
| 122 | + def test_bindings_added_and_removed(self): |
| 123 | + handler = dnd.dnd_start(self.source, FakeEvent(self.canvas)) |
| 124 | + self.assertIn('<Motion>', self.canvas.bind()) |
| 125 | + self.assertIn('<B1-ButtonRelease-1>', self.canvas.bind()) |
| 126 | + handler.cancel() |
| 127 | + self.assertNotIn('<Motion>', self.canvas.bind()) |
| 128 | + self.assertNotIn('<B1-ButtonRelease-1>', self.canvas.bind()) |
| 129 | + |
| 130 | + def test_switch_target(self): |
| 131 | + log1, log2 = [], [] |
| 132 | + w1, w2 = tkinter.Frame(self.root), tkinter.Frame(self.root) |
| 133 | + target1, target2 = Target(w1, log1), Target(w2, log2) |
| 134 | + handler = dnd.dnd_start(self.source, FakeEvent(self.canvas)) |
| 135 | + self.canvas.winfo_containing = lambda x, y: w1 |
| 136 | + handler.on_motion(FakeEvent(self.canvas)) # Enter target1. |
| 137 | + self.canvas.winfo_containing = lambda x, y: w2 |
| 138 | + handler.on_motion(FakeEvent(self.canvas)) # Leave target1, enter target2. |
| 139 | + self.assertIs(handler.target, target2) |
| 140 | + self.assertEqual(log1, ['accept', 'enter', 'leave']) |
| 141 | + self.assertEqual(log2, ['accept', 'enter']) |
| 142 | + handler.cancel() |
| 143 | + |
| 144 | + def test_target_in_ancestor(self): |
| 145 | + # The widget under the pointer has no dnd_accept, but an ancestor |
| 146 | + # does: the search walks up the master chain to find it. |
| 147 | + parent = tkinter.Frame(self.root) |
| 148 | + target = Target(parent, self.log) |
| 149 | + child = tkinter.Frame(parent) |
| 150 | + self.canvas.winfo_containing = lambda x, y: child |
| 151 | + handler = dnd.dnd_start(self.source, FakeEvent(self.canvas)) |
| 152 | + handler.on_motion(FakeEvent(self.canvas)) |
| 153 | + self.assertIs(handler.target, target) |
| 154 | + self.assertEqual(self.log, ['accept', 'enter']) |
| 155 | + handler.cancel() |
| 156 | + |
| 157 | + def test_accept_returning_none_continues(self): |
| 158 | + # dnd_accept() returning None means "not me, keep looking up". |
| 159 | + parent = tkinter.Frame(self.root) |
| 160 | + target = Target(parent, self.log) |
| 161 | + child = tkinter.Frame(parent) |
| 162 | + child.dnd_accept = lambda source, event: None |
| 163 | + self.canvas.winfo_containing = lambda x, y: child |
| 164 | + handler = dnd.dnd_start(self.source, FakeEvent(self.canvas)) |
| 165 | + handler.on_motion(FakeEvent(self.canvas)) |
| 166 | + self.assertIs(handler.target, target) |
| 167 | + handler.cancel() |
| 168 | + |
96 | 169 |
|
97 | 170 | if __name__ == "__main__": |
98 | 171 | unittest.main() |