@@ -60,6 +60,15 @@ def setUp(self):
6060 self .source = Source (self .log )
6161 self .target = Target (self .canvas , self .log )
6262
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+
6372 def test_drag_and_drop (self ):
6473 handler = dnd .dnd_start (self .source , FakeEvent (self .canvas ))
6574 self .assertIsNotNone (handler )
@@ -93,6 +102,70 @@ def test_no_recursive_start(self):
93102 def test_high_button_number_ignored (self ):
94103 self .assertIsNone (dnd .dnd_start (self .source , FakeEvent (self .canvas , num = 6 )))
95104
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+
96169
97170if __name__ == "__main__" :
98171 unittest .main ()
0 commit comments