[PATCH] winex11: The correct way to search the target window for drag&drop operations.
Zebediah Figura
z.figura12 at gmail.com
Fri Apr 19 15:43:46 CDT 2019
On 4/19/19 3:28 AM, John Found wrote:
> On Thu, 18 Apr 2019 12:53:35 -0500
> Zebediah Figura <z.figura12 at gmail.com> wrote:
>
>> On 04/18/2019 12:35 PM, John Found wrote:
>>> On Thu, 18 Apr 2019 12:09:02 -0500
>>> Zebediah Figura <z.figura12 at gmail.com> wrote:
>>>
>>>>
>>>> Really? I'm looking at this test right here:
>>>>
>>>> <https://source.winehq.org/git/wine.git/blob/refs/heads/master:/dlls/user32/tests/win.c#l9398>
>>>>
>>>
>>> Well, yes, I am wrong about the WindowFromPoint, but still only the first level of the children is checked.
>>>
>>> The mentioned test checks exactly this in 3 cases:
>>>
>>> 1. Main window without child created - main window returned.
>>> 2. Main window with single STATIC child created - main window returned. (correct, according to MSDN)
>>> 3. Main window with single BUTTON child created - the child returned.
>>>
>>> There is no child-in-child cases (and IMHO, should not be).
>>>
>>
>> Really? Why not? Our implementation is recursive, so it should return
>> the deepest child. If it's wrong, we should add tests to prove it.
>>
>>
>
> Well, I finished my extended tests and they are contradictory.
>
> 1. Yes, WindowFromPoint searches recursively on random depth, both in Windows and WINE.
> I was totally wrong about this. Excuse my ignorance.
>
> 2. But WindowFromPoint still does not work properly in my drag&drop patch. The problem is that WindowFromPoint
> fully ignores the STATIC controls (documented and correct behavior), but in Windows the STATIC controls are
> valid drop target if they have WS_EX_ACCEPTFILES set.
>
> This way, the implemented in the patch "window_from_point_dnd" function correctly returns the STATIC windows drop targets.
> Comparing with the original Windows behavior, I can't see any differences.
>
> (I will attach the improved test application I used, now it has STATIC as a drop targets and WindowFromPoint test tool as well.)
>
> So, the question is what to do now?
>
> Regards.
>
>
>
Thanks for testing, that makes sense. In that case it seems your
original approach is necessary. I would recommend adding a comment to
clarify this, so that someone doesn't make the mistake of trying to
replace it with WindowFromPoint() in the future.
I also have some stylistic comments on your original patch:
> +/* the recursive worker for window_from_point_dnd */
> +HWND do_window_from_point_dnd(HWND hwnd, POINT* point)
> +{
> + HWND w;
> + w = ChildWindowFromPointEx(hwnd, *point, CWP_SKIPDISABLED | CWP_SKIPINVISIBLE);
> + if (w && (w != hwnd))
> + {
> + MapWindowPoints(hwnd, w, point, 1);
> + w = do_window_from_point_dnd(w, point);
> + }
> + return w;
> +}
> +
> +
> +/* Recursively search for the window on given coordinates in a drag&drop specific manner. */
> +HWND window_from_point_dnd(HWND hwnd, POINT point)
> +{
> + POINT p;
> + p.x = point.x;
> + p.y = point.y;
> + ScreenToClient(hwnd, &p);
> + return do_window_from_point_dnd(hwnd, &p);
> +}
> +
These functions should be static, since they're not used elsewhere.
I also feel like this would more readable formatted as a loop instead of
a recursive function. Something like:
/* Find the deepest child window at the given point. We can't use
WindowFromPoint() for this, because it skips HTTRANSPARENT windows, and
those should still receive drop notifications. */
static HWND window_from_point( HWND hwnd, POINT point )
{
HWND child;
ScreenToClient( hwnd, &point );
while ((child = ChildWindowFromPointEx( hwnd, point,
CWP_SKIPDISABLED | CWP_SKIPINVISIBLE )) && child != hwnd)
{
MapWindowPoints( hwnd, child, &point, 1 );
hwnd = child;
}
return hwnd;
}
> + while (targetWindow && !(GetWindowLongW(targetWindow, GWL_EXSTYLE) & WS_EX_ACCEPTFILES))
> + targetWindow = GetParent(targetWindow);
...
> + while (hwnd_drop && !(GetWindowLongW(hwnd_drop, GWL_EXSTYLE) & WS_EX_ACCEPTFILES))
> + hwnd_drop = GetParent(hwnd_drop);
Since you do this in both places, you could also factor it into the
window_from_point helper, in which case it might be better to call it
something like get_drop_target().
> + POINT pt;
> + HWND hwnd_drop;
> +
> + pt.x = XDNDxy.x;
> + pt.y = XDNDxy.y;
> +
> + hwnd_drop = window_from_point_dnd(hWnd, pt);
You're passing pt by value, so you don't need to copy it into a local
variable.
More information about the wine-devel
mailing list