Merge branch 'fix-dnd-coordinates-on-windows' into 'master'

Fix DND coordinates on Windows

See merge request GNOME/gtk!3930
This commit is contained in:
Benjamin Otte 2021-10-16 17:04:26 +00:00
commit cf7ee82755
3 changed files with 71 additions and 11 deletions

View File

@ -2082,8 +2082,8 @@ gdk_dnd_handle_motion_event (GdkDrag *drag,
API_CALL (PostThreadMessage, (clipdrop->dnd_thread_id,
WM_MOUSEMOVE,
key_state,
MAKELPARAM (x * drag_win32->scale,
y * drag_win32->scale)));
MAKELPARAM (x_root * drag_win32->scale - _gdk_offset_x,
y_root * drag_win32->scale - _gdk_offset_y)));
return TRUE;
}

View File

@ -425,6 +425,31 @@ set_source_actions_helper (GdkDrop *drop,
return actions;
}
/* Utility function to translate win32 screen coordinates to
* client coordinates (i.e. relative to the surface origin)
*
* Note that input is expected to be:
* a) NOT scaled by dpi_scale
* b) NOT translated by the GDK screen offset (gdk_offset_x / y)
*
* This utility function preserves subpixel precision
*/
static void
unscaled_screen_to_client (GdkSurface* surface,
double screen_x,
double screen_y,
double *client_x,
double *client_y)
{
POINT client_origin;
client_origin.x = 0;
client_origin.y = 0;
ClientToScreen (GDK_SURFACE_HWND (surface), &client_origin);
*client_x = screen_x - client_origin.x;
*client_y = screen_y - client_origin.y;
}
/* The pdwEffect here initially points
* to a DWORD that contains the value of dwOKEffects argument in DoDragDrop,
* i.e. the drag action that the drag source deems acceptable.
@ -445,6 +470,8 @@ idroptarget_dragenter (LPDROPTARGET This,
GdkDisplay *display;
int pt_x;
int pt_y;
double x = 0.0;
double y = 0.0;
GdkDrag *drag;
GdkDragAction source_actions;
GdkDragAction dest_actions;
@ -489,7 +516,12 @@ idroptarget_dragenter (LPDROPTARGET This,
set_data_object (&ctx->data_object, pDataObj);
pt_x = pt.x / drop_win32->scale + _gdk_offset_x;
pt_y = pt.y / drop_win32->scale + _gdk_offset_y;
gdk_drop_emit_enter_event (drop, TRUE, pt_x, pt_y, GDK_CURRENT_TIME);
unscaled_screen_to_client (ctx->surface, pt.x, pt.y, &x, &y);
x /= drop_win32->scale;
y /= drop_win32->scale;
gdk_drop_emit_enter_event (drop, TRUE, x, y, GDK_CURRENT_TIME);
drop_win32->last_key_state = grfKeyState;
drop_win32->last_x = pt_x;
drop_win32->last_y = pt_y;
@ -544,7 +576,14 @@ idroptarget_dragover (LPDROPTARGET This,
pt_y != drop_win32->last_y ||
grfKeyState != drop_win32->last_key_state)
{
gdk_drop_emit_motion_event (ctx->drop, TRUE, pt_x, pt_y, GDK_CURRENT_TIME);
double x = 0.0;
double y = 0.0;
unscaled_screen_to_client (ctx->surface, pt.x, pt.y, &x, &y);
x /= drop_win32->scale;
y /= drop_win32->scale;
gdk_drop_emit_motion_event (ctx->drop, TRUE, x, y, GDK_CURRENT_TIME);
drop_win32->last_key_state = grfKeyState;
drop_win32->last_x = pt_x;
drop_win32->last_y = pt_y;
@ -587,6 +626,8 @@ idroptarget_drop (LPDROPTARGET This,
GdkWin32Drop *drop_win32 = GDK_WIN32_DROP (ctx->drop);
int pt_x = pt.x / drop_win32->scale + _gdk_offset_x;
int pt_y = pt.y / drop_win32->scale + _gdk_offset_y;
double x = 0.0;
double y = 0.0;
GdkDragAction dest_action;
GDK_NOTE (DND, g_print ("idroptarget_drop %p ", This));
@ -606,7 +647,12 @@ idroptarget_drop (LPDROPTARGET This,
grfKeyState);
drop_win32->drop_finished = FALSE;
gdk_drop_emit_drop_event (ctx->drop, TRUE, pt_x, pt_y, GDK_CURRENT_TIME);
unscaled_screen_to_client (ctx->surface, pt.x, pt.y, &x, &y);
x /= drop_win32->scale;
y /= drop_win32->scale;
gdk_drop_emit_drop_event (ctx->drop, TRUE, x, y, GDK_CURRENT_TIME);
while (!drop_win32->drop_finished)
g_main_context_iteration (NULL, FALSE);

View File

@ -584,12 +584,6 @@ _gdk_win32_display_create_surface (GdkDisplay *display,
if (!title || !*title)
title = "";
/* WS_EX_TRANSPARENT means "try draw this window last, and ignore input".
* It's the last part we're after. We don't want DND indicator to accept
* input, because that will make it a potential drop target, and if it's
* under the mouse cursor, this will kill any DND.
*/
klass = RegisterGdkClass (surface_type);
wtitle = g_utf8_to_utf16 (title, -1, NULL, NULL, NULL);
@ -652,6 +646,7 @@ _gdk_win32_display_create_surface (GdkDisplay *display,
_gdk_win32_surface_enable_transparency (surface);
_gdk_win32_surface_register_dnd (surface);
_gdk_win32_surface_update_style_bits (surface);
g_signal_connect (frame_clock,
"after-paint",
@ -1838,6 +1833,8 @@ _gdk_win32_surface_update_style_bits (GdkSurface *window)
RECT rect, before, after;
gboolean was_topmost;
gboolean will_be_topmost;
gboolean was_layered;
gboolean will_be_layered;
HWND insert_after;
UINT flags;
@ -1852,7 +1849,9 @@ _gdk_win32_surface_update_style_bits (GdkSurface *window)
AdjustWindowRectEx (&before, old_style, FALSE, old_exstyle);
was_topmost = (old_exstyle & WS_EX_TOPMOST) ? TRUE : FALSE;
was_layered = (old_exstyle & WS_EX_LAYERED) ? TRUE : FALSE;
will_be_topmost = was_topmost;
will_be_layered = was_layered;
old_exstyle &= ~WS_EX_TOPMOST;
@ -1862,7 +1861,14 @@ _gdk_win32_surface_update_style_bits (GdkSurface *window)
if (GDK_IS_DRAG_SURFACE (window))
{
new_exstyle |= WS_EX_TOOLWINDOW;
/* WS_EX_LAYERED | WS_EX_TRANSPARENT makes the drag surface behave
* in pointer input passthrough mode, so it doesn't interfere with
* the drag and drop operation.
*/
new_exstyle |= WS_EX_LAYERED | WS_EX_TRANSPARENT;
will_be_topmost = TRUE;
will_be_layered = TRUE;
}
else
{
@ -1907,6 +1913,14 @@ _gdk_win32_surface_update_style_bits (GdkSurface *window)
_gdk_win32_surface_exstyle_to_string (new_exstyle)));
SetWindowLong (GDK_SURFACE_HWND (window), GWL_EXSTYLE, new_exstyle);
if (!was_layered && will_be_layered)
{
/* We have to call SetLayeredWindowAttributes when setting the
* WS_EX_LAYERED style anew, otherwise the window won't show up
*/
API_CALL (SetLayeredWindowAttributes, (GDK_SURFACE_HWND (window), 0, 255, LWA_ALPHA));
}
}
AdjustWindowRectEx (&after, new_style, FALSE, new_exstyle);