GDK W32: Force correct mouse cursor for custom resize/move
* Explicitly grab the device, setting appropriate cursor on it. * Fix gdk_device_virtual_set_window_cursor() to just set the cursor, without trying to check that mouse is over the given window. Also prevent it from immediately resetting cursor. * Alse take into account things that happen in other parts of GDK - don't look for replacement cursor, GDK already did that, and don't create a default arrow cursor instead of NULL, GDK-W32 already did that up the stack as well. Warn about inappropriate cursor == NULL argument instead. https://bugzilla.gnome.org/show_bug.cgi?id=762711
This commit is contained in:
@ -94,7 +94,6 @@ gdk_device_virtual_set_window_cursor (GdkDevice *device,
|
||||
{
|
||||
GdkWindow *parent_window;
|
||||
GdkWindowImplWin32 *impl;
|
||||
GdkCursor *replacement_cursor;
|
||||
GdkCursor *previous_cursor;
|
||||
|
||||
impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
|
||||
@ -103,55 +102,27 @@ gdk_device_virtual_set_window_cursor (GdkDevice *device,
|
||||
|
||||
if (cursor != NULL && GDK_WIN32_CURSOR (cursor)->hcursor != NULL)
|
||||
{
|
||||
/* If the pointer is over our window, set new cursor */
|
||||
GdkWindow *curr_window = gdk_window_get_device_position (window, device, NULL, NULL, NULL);
|
||||
|
||||
if (curr_window == window ||
|
||||
(curr_window && window == gdk_window_get_toplevel (curr_window)))
|
||||
SetCursor (GDK_WIN32_CURSOR (cursor)->hcursor);
|
||||
else
|
||||
{
|
||||
/* Climb up the tree and find whether our window is the
|
||||
* first ancestor that has cursor defined, and if so, set
|
||||
* new cursor.
|
||||
*/
|
||||
while (curr_window && curr_window->impl &&
|
||||
!GDK_WINDOW_IMPL_WIN32 (curr_window->impl)->cursor)
|
||||
{
|
||||
curr_window = curr_window->parent;
|
||||
if (curr_window == GDK_WINDOW (window))
|
||||
{
|
||||
SetCursor (GDK_WIN32_CURSOR (cursor)->hcursor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Unset the previous cursor: Need to make sure it's no longer in
|
||||
* use before we destroy it, in case we're not over our window but
|
||||
* the cursor is still set to our old one.
|
||||
*/
|
||||
if (previous_cursor != NULL &&
|
||||
else if (previous_cursor != NULL &&
|
||||
GetCursor () == GDK_WIN32_CURSOR (previous_cursor)->hcursor)
|
||||
{
|
||||
/* Look for a suitable cursor to use instead */
|
||||
replacement_cursor = NULL;
|
||||
parent_window = GDK_WINDOW (window)->parent;
|
||||
|
||||
while (replacement_cursor == NULL)
|
||||
{
|
||||
if (parent_window)
|
||||
{
|
||||
impl = GDK_WINDOW_IMPL_WIN32 (parent_window->impl);
|
||||
replacement_cursor = impl->cursor;
|
||||
parent_window = parent_window->parent;
|
||||
/* The caller will unref previous_cursor shortly,
|
||||
* but it holds the handle to currently-used cursor,
|
||||
* and we can't call SetCursor(NULL).
|
||||
*/
|
||||
g_warning (G_STRLOC ": Refusing to replace cursor %p (handle %p) with NULL. "
|
||||
"Expect ugly results.",
|
||||
previous_cursor, GDK_WIN32_CURSOR (previous_cursor)->hcursor);
|
||||
}
|
||||
else
|
||||
replacement_cursor = _gdk_win32_display_get_cursor_for_type (gdk_device_get_display (device), GDK_LEFT_PTR);
|
||||
}
|
||||
|
||||
SetCursor (GDK_WIN32_CURSOR (replacement_cursor)->hcursor);
|
||||
{
|
||||
/* Up the stack all effors were made already to ensure that
|
||||
* the "cursor" argument is non-NULL.
|
||||
* If it is, calling SetCursor(NULL) is absolutely not
|
||||
* the right decision, so we just warn and bail out.
|
||||
*/
|
||||
g_warning (G_STRLOC ": Refusing to set NULL cursor");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2460,6 +2460,8 @@ gdk_event_translate (MSG *msg,
|
||||
/* We keep the implicit grab until no buttons at all are held down */
|
||||
if ((state & GDK_ANY_BUTTON_MASK & ~(GDK_BUTTON1_MASK << (button - 1))) == 0)
|
||||
{
|
||||
GdkWindow *native_window = pointer_grab->native_window;
|
||||
|
||||
ReleaseCapture ();
|
||||
|
||||
new_window = NULL;
|
||||
@ -2473,8 +2475,9 @@ gdk_event_translate (MSG *msg,
|
||||
if (PtInRect (&rect, client_pt))
|
||||
new_window = gdk_win32_handle_table_lookup (hwnd);
|
||||
}
|
||||
|
||||
synthesize_crossing_events (display,
|
||||
pointer_grab->native_window, new_window,
|
||||
native_window, new_window,
|
||||
GDK_CROSSING_UNGRAB,
|
||||
&msg->pt,
|
||||
0, /* TODO: Set right mask */
|
||||
@ -2488,6 +2491,13 @@ gdk_event_translate (MSG *msg,
|
||||
generate_button_event (GDK_BUTTON_RELEASE, button,
|
||||
window, msg);
|
||||
|
||||
impl = GDK_WINDOW_IMPL_WIN32 (window->impl);
|
||||
|
||||
/* End a drag op when the same button that started it is released */
|
||||
if (impl->drag_move_resize_context.op != GDK_WIN32_DRAGOP_NONE &&
|
||||
impl->drag_move_resize_context.button == button)
|
||||
gdk_win32_window_end_move_resize_drag (window);
|
||||
|
||||
return_val = TRUE;
|
||||
break;
|
||||
|
||||
|
||||
@ -2062,17 +2062,18 @@ gdk_win32_window_set_device_cursor (GdkWindow *window,
|
||||
*/
|
||||
previous_cursor = impl->cursor;
|
||||
|
||||
GDK_DEVICE_GET_CLASS (device)->set_window_cursor (device, window, cursor);
|
||||
|
||||
if (cursor)
|
||||
impl->cursor = g_object_ref (cursor);
|
||||
else
|
||||
/* Use default cursor otherwise. Setting it to NULL will make it use
|
||||
* system-default cursor, which is not controlled by GTK cursor theming.
|
||||
/* Use default cursor otherwise. Don't just set NULL cursor,
|
||||
* because that will just hide the cursor, which is not
|
||||
* what the caller probably wanted.
|
||||
*/
|
||||
impl->cursor = _gdk_win32_display_get_cursor_for_type (gdk_display_get_default (),
|
||||
impl->cursor = _gdk_win32_display_get_cursor_for_type (gdk_device_get_display (device),
|
||||
GDK_LEFT_PTR);
|
||||
|
||||
GDK_DEVICE_GET_CLASS (device)->set_window_cursor (device, window, impl->cursor);
|
||||
|
||||
/* Destroy the previous cursor */
|
||||
if (previous_cursor != NULL)
|
||||
g_object_unref (previous_cursor);
|
||||
@ -2823,6 +2824,49 @@ _gdk_window_get_functions (GdkWindow *window,
|
||||
return (functions_set != NULL);
|
||||
}
|
||||
|
||||
static const gchar *
|
||||
get_cursor_name_from_op (GdkW32WindowDragOp op,
|
||||
GdkWindowEdge edge)
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case GDK_WIN32_DRAGOP_MOVE:
|
||||
return "move";
|
||||
case GDK_WIN32_DRAGOP_RESIZE:
|
||||
switch (edge)
|
||||
{
|
||||
case GDK_WINDOW_EDGE_NORTH_WEST:
|
||||
return "nw-resize";
|
||||
case GDK_WINDOW_EDGE_NORTH:
|
||||
return "n-resize";
|
||||
case GDK_WINDOW_EDGE_NORTH_EAST:
|
||||
return "ne-resize";
|
||||
case GDK_WINDOW_EDGE_WEST:
|
||||
return "w-resize";
|
||||
case GDK_WINDOW_EDGE_EAST:
|
||||
return "e-resize";
|
||||
case GDK_WINDOW_EDGE_SOUTH_WEST:
|
||||
return "sw-resize";
|
||||
case GDK_WINDOW_EDGE_SOUTH:
|
||||
return "s-resize";
|
||||
case GDK_WINDOW_EDGE_SOUTH_EAST:
|
||||
return "e-resize";
|
||||
}
|
||||
/* default: warn about unhandled enum values,
|
||||
* fallthrough to GDK_WIN32_DRAGOP_NONE case
|
||||
*/
|
||||
case GDK_WIN32_DRAGOP_COUNT:
|
||||
g_assert_not_reached ();
|
||||
case GDK_WIN32_DRAGOP_NONE:
|
||||
return "default";
|
||||
/* default: warn about unhandled enum values */
|
||||
}
|
||||
|
||||
g_assert_not_reached ();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
setup_drag_move_resize_context (GdkWindow *window,
|
||||
GdkW32DragMoveResizeContext *context,
|
||||
@ -2835,9 +2879,33 @@ setup_drag_move_resize_context (GdkWindow *window,
|
||||
guint32 timestamp)
|
||||
{
|
||||
RECT rect;
|
||||
const gchar *cursor_name;
|
||||
gint x, y;
|
||||
GdkWindow *pointer_window;
|
||||
GdkDisplay *display = gdk_device_get_display (device);
|
||||
|
||||
_gdk_win32_get_window_rect (window, &rect);
|
||||
|
||||
cursor_name = get_cursor_name_from_op (op, edge);
|
||||
|
||||
context->cursor = _gdk_win32_display_get_cursor_for_name (display, cursor_name);
|
||||
|
||||
gdk_window_get_root_origin (window, &x, &y);
|
||||
x -= root_x;
|
||||
y -= root_y;
|
||||
pointer_window = gdk_device_get_window_at_position (device, &x, &y);
|
||||
|
||||
/* Note: This triggers a WM_CAPTURECHANGED, which will trigger
|
||||
* gdk_win32_window_end_move_resize_drag(), which will end
|
||||
* our op before it even begins, but only if context->op is not NONE.
|
||||
* This is why we first do the grab, *then* set the op.
|
||||
*/
|
||||
gdk_device_grab (device, pointer_window,
|
||||
GDK_OWNERSHIP_NONE, FALSE,
|
||||
GDK_ALL_EVENTS_MASK,
|
||||
context->cursor,
|
||||
timestamp);
|
||||
|
||||
context->op = op;
|
||||
context->edge = edge;
|
||||
context->device = device;
|
||||
@ -2848,9 +2916,10 @@ setup_drag_move_resize_context (GdkWindow *window,
|
||||
context->start_rect = rect;
|
||||
|
||||
GDK_NOTE (EVENTS,
|
||||
g_print ("begin drag moveresize: "
|
||||
g_print ("begin drag moveresize: window %p, toplevel %p, "
|
||||
"op %u, edge %d, device %p, "
|
||||
"button %d, coord %d:%d, time %u\n",
|
||||
window, gdk_window_get_toplevel (window),
|
||||
context->op, context->edge, context->device,
|
||||
context->button, context->start_root_x,
|
||||
context->start_root_y, context->timestamp));
|
||||
@ -2863,10 +2932,16 @@ gdk_win32_window_end_move_resize_drag (GdkWindow *window)
|
||||
GdkW32DragMoveResizeContext *context = &impl->drag_move_resize_context;
|
||||
|
||||
context->op = GDK_WIN32_DRAGOP_NONE;
|
||||
|
||||
gdk_device_ungrab (context->device, GDK_CURRENT_TIME);
|
||||
|
||||
g_clear_object (&context->cursor);
|
||||
|
||||
GDK_NOTE (EVENTS,
|
||||
g_print ("end drag moveresize: "
|
||||
g_print ("end drag moveresize: window %p, toplevel %p,"
|
||||
"op %u, edge %d, device %p, "
|
||||
"button %d, coord %d:%d, time %u\n",
|
||||
window, gdk_window_get_toplevel (window),
|
||||
context->op, context->edge, context->device,
|
||||
context->button, context->start_root_x,
|
||||
context->start_root_y, context->timestamp));
|
||||
|
||||
@ -64,10 +64,15 @@ struct _GdkW32DragMoveResizeContext
|
||||
/* The edge that was grabbed for resizing. Not used for moving. */
|
||||
GdkWindowEdge edge;
|
||||
|
||||
/* Not used */
|
||||
/* The device used to initiate the op.
|
||||
* We grab it at the beginning and ungrab it at the end.
|
||||
*/
|
||||
GdkDevice *device;
|
||||
|
||||
/* Not used */
|
||||
/* The button pressed down to initiate the op.
|
||||
* The op will be canceled only when *this* button
|
||||
* is released.
|
||||
*/
|
||||
gint button;
|
||||
|
||||
/* Initial cursor position when the operation began.
|
||||
@ -89,6 +94,9 @@ struct _GdkW32DragMoveResizeContext
|
||||
* the window size and poistion to the native window.
|
||||
*/
|
||||
gboolean native_move_resize_pending;
|
||||
|
||||
/* The cursor we should use while the operation is running. */
|
||||
GdkCursor *cursor;
|
||||
};
|
||||
|
||||
typedef struct _GdkW32DragMoveResizeContext GdkW32DragMoveResizeContext;
|
||||
|
||||
Reference in New Issue
Block a user