Tk Source Code

View Ticket
Login
Ticket UUID: 855ec435ef50de090e6eea98f6ff829d7faed7ce
Title: BWidget Drag & Drop no longer works with Tk 9.1a0 on Aqua
Type: Bug Version: trunk
Submitter: nemethi Created on: 2025-04-06 11:26:45
Subsystem: 66. Aqua Window Operations Assigned To: marc_culler
Priority: 5 Medium Severity: Important
Status: Closed Last Modified: 2025-04-11 17:30:21
Resolution: Fixed Closed By: nemethi
    Closed on: 2025-04-11 17:30:21
Description:

The attached simple script uses the Drag & Drop functionality of BWidget.  It works as expected with all Tk releases, including 9.0.0 and 9.0.1.  However, if Tk built from the current trunk is being used on Aqua then after a drag operation the left mouse button gets insensitive: neither a second drag, nor a click into the listbox has any effect.  This holds true regardless of whether the drag was terminated by a drop into the listbox widget (registered as drop target) or by releasing the mouse button outside the listbox.  After clicking anywhere outside the GUI and then on the GUI itself, the mouse works again as expected, but only until repeating the drag.

For the test with Tcl/Tk 9 one needs the most recent BWidget version 1.10.1, which can be downloaded from

https://sourceforge.net/projects/tcllib/files/BWidget/1.10.1

User Comments: nemethi (claiming to be Csaba Nemethi) added on 2025-04-11 17:30:21:

Yes, only macOS is affected by this issue.


marc_culler (claiming to be Marc Culler) added on 2025-04-11 17:22:47:
Thanks, Csaba.

> BTW: I have just found out that the issue regarding the insensitivity
> of the mouse after a drag with TkDND was introduced in Tk 8.6.13.

That was a while ago.  Do you know if it is only macOS that is affected?

I think I will treat that as a different problem, and close this ticket.

nemethi (claiming to be Csaba Nemethi) added on 2025-04-11 16:56:48:
Many thanks Marc, the test script with BWidget still works as expected for me, too.

The test script witk TkDND behaves exactly as before. BTW: I have just found out that the issue regarding the insensitivity of the mouse after a drag with TkDND was introduced in Tk 8.6.13.

marc_culler (claiming to be Marc Culler) added on 2025-04-11 16:05:21:
I moved the code which updates the TkEventTarget into the NSApp method
windowActivation, which gets called whenever an NSWindow accepts or rejects
the role of keyWindow.  I think that will be more robust, because any
call to [NSWindow makeKeyAndOrderFront] will result in a correct assignment
of the current TkEventTarget.

The test script still works for me.  Csaba, if you have a moment to test
the new tip of the bugfix branch it would be appreciated.  Thanks.

marc_culler (claiming to be Marc Culler) added on 2025-04-11 13:00:35:
Thanks, Csaba.

The code that was involved here was trying to reassign focus to the top
remaining window when a window is destroyed.  The window being destroyed
is thr small window containing a drag icon.  It gets destroyed on the drop.
The code was resetting the macOS KeyWindow, but it was not correctly
notifying Tk that a new Tk window should receive the focus.  It is not
sufficient to call [NSWindow makeKeyAndOrderFront], it seems.  I don't
understand why not.  There is a callback that Apple calls when a new
window becomes the KeyWindow.  It should handle that job.

So I think I need to investigate a little further and see if I can make
the fix more robust.  Maybe that will fix tkdnd as well.  I think it is
a similar issue, just at the C level.

nemethi (claiming to be Csaba Nemethi) added on 2025-04-11 09:38:48:

Many thanks Marc, your bugfix works for me like a charm!

One more detail regarding the similar issue with TkDND (i.e., that after a terminated drag one must click somewhere on the GUI to make the mouse sensitive again): This problem is much older than Tk 9.1a0. It was already present in Tk 9.0.0, possibly even in earlier Tk versions.


marc_culler (claiming to be Marc Culler) added on 2025-04-10 21:18:19:
The BWidget issue seems to be fixed in the new bugfix branch bug-855ec435ef.
The tkdnd issue is not fixed in that branch.  In spite of having very
similar (but not quite identical) symptoms these must be two different
problems.

The bug appeared in commit [d9df72b3], the first commit to the branch
aqua_key_window on 2025-03-12.

Would you please test the bugfix branch?  Thanks.

nemethi (claiming to be Csaba Nemethi) added on 2025-04-10 17:11:36:

Yet another remark: It is true that after a terminated drag the mouse becomes insensitive not only with BWidget, but also with TkDND. OTOH, the way to make it work again, is not exactly the same: With BWidget the user has to click somwhere outside the GUI and then on the GUI itself. With TkDND it is sufficient to click on the GUI (w/o having first klicked somewhere outside it).


marc_culler (claiming to be Marc Culler) added on 2025-04-10 17:05:06:
Well, I didn't quite do what I said.  I couldn't abandon the TkDND issue
so fast.

I can now confirm that Nicolas' TkDndTest.tcl script can be fixed (.i.e.
that the indicator is shown correctly during the drag operation) by
simply adding a call to update idletasks at the ends of the procs
onDropEnterOrPos and onDropLeave.  I definitely am not interested in
adding an expensive inner event loop inside of updateLayer to solve
a problem that is easily fixed by calling update idletasks, which is
equivalent to running the inner event loop:
   while(Tcl_DoOneEvent(TCL_IDLE_EVENTS)){}
It is a fact of life that sometimes it is necessary to call update or
update idletasks to make Tk behave as expected.  This is just one of
those times.

I will move on to the BWidgets issue (which is also arising with the
TkDndTest.tcl script and probably has the same cause).  I will start
as usual by doing bisection to find which commit first showed the problem.
Csaba says it was after the release of 9.0.1.

nemethi (claiming to be Csaba Nemethi) added on 2025-04-10 16:52:06:

The "target indicator" is just a way ("invented" by me) to indicate where the drop will insert the dragged data. The fact that no insertion after the last listbox element takes place is due exclusively to the simplicity of the test script and has nothing to do with TkDND. With some additional code, the test script could also append the data to the listbox rather than insert it before the last element. The test script works as you observed mainly because it uses the nearest command of the listbox, which returns the index of the last element, regardless of whether the pointer is above or below the last element.


marc_culler (claiming to be Marc Culler) added on 2025-04-10 16:30:55:
I have another question unrelated to event loops and the like.  When I
run the tkdnd test script it is not possible to insert an item at the end
of the list.  When the event loop is re-enabled, and the indicator is
drawn, it never gets drawn after the last element, and doing the drop below
the last line in the listbox inserts the item in the second-to-last position
not at the end.

QUESTION: Is this expected behavior from tkdnd?  Why?

marc_culler (claiming to be Marc Culler) added on 2025-04-10 16:26:41:
I can confirm that adding the event loop does make the indicator appear
as expected with the tkdnd demo script.  I suspect that the best way
to fix this would be for tkdnd to run its own event loop, rather than
expecting Tk to do it.  So far there are 0 known cases where the event
loop is needed for the Tk core and 1 case where one particular 
extension needs it. Having the extension provide its own event loop
seems like a better alternative than slowing down the updateLayer method
for every drawing operation in the Tk core, whether or not that extension
is in use.

I see that one idle task gets processed by the loop, presumably the
display proc for the indicator.  So another fix might involve adding a
call to update idletasks at an appropriate place in the listbox tcl code.

I will look into this.  But first I will focus on the BWidget issue and
see if I can figure out what is going on there.  I don't think it is a
coincidence that the exact same problem with the mouse button occurs for
the pure tcl BWidget script and for the pure C tkdnd implementation.
Surely the two identical problems in those two wildly different contexts
have the same underlying cause.  (And restoring the inner event loop does
not fix the problem that the second drag-and-drop action ignores the
mouse drag.)

marc_culler (claiming to be Marc Culler) added on 2025-04-10 15:57:43:
I'd like to explain why I am reluctant to restore the inner event loop,
i.e the call:
   while(Tcl_DoOneEvent(TCL_IDLE_EVENTS)){}
which is involved in the change that Nicolas suggests.

That event loop was running inside the updateLayer method, which is the
method that swaps the NSView CGImage backing store with one which has the
latest drawing operations rendered.  The number of calls to updateLayer is
huge.  That method needs to be as efficient as possible.  Transferring
control to the Tcl event loop to process an arbitrary number of idle tasks
every time that updateLayer is called is pretty far from efficient.

It was clear that the event loop needed to run when a window was first
opened, but otherwise I could find no evidence that it was ever needed.
It has been gone for quite a while, and this subtle issue with tkdnd is
the first indication of any bad side effect from making updateLayer more
efficient.

Rather than restore the extra event loop to run for every call to
updateLayer,  I would prefer to understand why this is happening.
Blindly inserting event loops here and there whenever there is a small
graphics issue is not a good way of doing things.

nemethi (claiming to be Csaba Nemethi) added on 2025-04-10 15:16:18:

This ticket in about a behavior related to Drag & Drop via BWidget: After a terminated drag the left mouse button becomes insensitive. This happens only with the current trunk version of Tk. I have tested that the attached sample script works as expected with Tk 9.0.0 and 9.0.1, but behaves as described above with the current trunk. All the 3 tests were run on macOs Sequoia 15.3.2, which seems to contradict your assumption that the issue might be related to an OS change.

The other problem, reported to you by Nicolas in a private mail, is related to Drag & Drop via TkDND: During a drag the target indicator remains invisible. The target indicator is a thin frame place'd before the nearest item of the tablelist widget registered as drop target, or before the nearest element of the listbox registered as drop target. AFAIK, Nicolas has forwarded to you a test script involving tablelist widgets and later a very simple one independent from Tablelist. I suggest you to take a closer look at the second test script, because it is really quite simple and will help you understand what he target indicator is. With Tk 9.0.0 and 9.0.1 the target indicator is displayed as expected, but with the current trunk version it remains invisible. The change proposed by Nicolas fixes this issue.


nab added on 2025-04-10 14:57:21:
Hi Marc,
here I'm running macOS15

with the latest script I sent you (TkDndTest.tcl), when you click on drag source, maintain the click and drag to the listbox, you should see a line that indicate where insertion will occur.
this is the 'target indicator'

with Tk's code as it is you don't see it.
with change in tkMacOSXWindowEvent.c, you can see it

++

marc_culler (claiming to be Marc Culler) added on 2025-04-10 14:03:22:
Well, restoring a hack which should never have been necessary is not
exactly what I think of as a solution.  And I am not sure it works.

But I would like to understand both examples.  I need some help.  They both
seem to show the same behavior to me.  Can you explain to me what the
"target indicator" is?

In both cases I see when I similar behavior when I "instrument" the code.
It has to do with an aspect of mouse events which has always been tricky.
Namely, when a mouse drag occurs Apple does not send the equivalent of a
motion event with an indication that a mouse button is pressed.
Instead they send an NSLeftMouseDragged event if the mouse motion is done
while the left button is pressed and an NSMousedMoved event if the mouse is
moved without the button being pressed.  It is up to Tk to track the
information about which buttons are pressed.  That would be easy if Apple
sent, say, an NSLeftMouseDown event when the left mouse button is pressed
and an NSLeftMouseUp event when the left mouse button is released.  But they
don't.  At least no consistently.  The XXXMouseDown and XXXMouseUp events
are not paired in the way that you would expect.  Often the XXXMouseUp is
not sent and Tk must somehow deduce that the mouse button was released from
the event stream.

This is why I said it might be related to an OS change.  For that reason it
would be helpful to know which OS version people are using when they see
these glitches.

nemethi (claiming to be Csaba Nemethi) added on 2025-04-10 09:00:02:

As mentioneed by Harald, this ticket is about the Drag & Drop functionality of BWidget.  The two test scripts that I sent to Nicolas are about a different issue, namely that the target indicator is not visible when running TkDND with Tk built from trunk (and Nicolas seems to have found the place in tkMacOSXWindowEvent.c which causes that problem).


marc_culler (claiming to be Marc Culler) added on 2025-04-09 23:38:59:
The fact that two completely different dnd implementations fail the same way
is a big hint suggesting that there is a simple explanation.  (E.g. the Tk
button state is not being maintained correctly, causing Tk to think that the
mouse button is still pressed after it is released.)

marc_culler (claiming to be Marc Culler) added on 2025-04-09 21:10:43:
I have a different test script, from Nicolas Bats via Csaba, which does not
require BWidgets to demonstrate the problem.

oehhar added on 2025-04-09 19:01:06:

Thanks, Marc, great that you look into this.

Just for the wording: Csaba reports about bwidgets, not tkdnd. Those are two distinct extensions.

TkDnD is a C extension interfacing with sources/targets outside of Tk. BWidget only handles DnD within Tk widgets.

Harald


marc_culler (claiming to be Marc Culler) added on 2025-04-09 18:44:23:
This appears to be a bug in tkdnd.

I suspect that tkdnd is designed to steal all NSLeftMouseDragged events
during a DND operation, and not pass them on to Tk. Perhaps they are setting
up their own modal event loop to handle those events.

When tkdnd appears to be working correctly, Tk only receives about 5
NSLeftMouseDragged events, even during a very long drag.  (There should be
one of those events generated each time that the mouse moves during the drag.)

When tkdnd is misbehaving on the second DND operation, Tk receives all of
the NSLeftMouseDragged events.  This probably means that tkdnd does not see
any of those events and so it doesn't know when the drag started or when the
drop happened.

Perhaps tkdnd is not able to start its private modal event loop on the
second DND because it did not correctly terminate the previous loop when
the drop happened.

It is possible that there was a change in macOS which caused this to stop
working.

nemethi (claiming to be [email protected]) added on 2025-04-07 15:30:59:

Unfortunately, I cannot tell you which commit introduced the issue, because it has been just a few days ago that after several years I tested the Drag & Drop again.


marc_culler (claiming to be Marc Culler) added on 2025-04-07 14:52:23:
Thanks Csaba. I see the issue.  I am investigating.

Do you happen to know which commit first showed this issue?

Attachments: